Compare commits

...

4 commits

Author SHA1 Message Date
Nuno Duque Nunes
f2972567c4 change messages for user 2026-06-09 23:15:44 +01:00
Nuno Duque Nunes
88f53dff32 add norank to wrank formatting 2026-06-05 03:59:26 +01:00
Nuno Duque Nunes
77c065114c merge dev 2026-06-04 03:35:22 +01:00
Nuno Duque Nunes
4c60ff20a6 update before dev merge 2026-06-04 02:00:50 +01:00
28 changed files with 182 additions and 568 deletions

16
.env
View file

@ -1,16 +0,0 @@
DISCORD_TOKEN=MTUxMDk1OTgxNDYyMzEwNTA0NA.GNY7A9.Boq4MruKRqvo1UZ5JmsCkYN7q1xCTNKuqyh1oA
POLL_CHANNEL_ID=1511006387293917355
RESULTS_CHANNEL_ID=1511006410627088544
SCORE_CHANNEL_ID=1511006435079884991
CLIENT_ID=1510959814623105044
GUILD_ID=1511006171681652858
EPHEMERAL_DELETE_MS=0
POLL_EPHEMERAL_ENABLED=false # voting messages (disabled during testing)
COMMAND_EPHEMERAL_ENABLED=true # command outputs (always on)
AUTO_VOTE_ON_CONFLICT_SWITCH=true
IMPERSONATE_RESET_ON_POLL=false
IMPERSONATE_INDICATOR=true
RECLAIM_NOTIFY_BORROWER=true
# Emoji upload servers
EMOJI_DONOR_GUILDS=1511903882224336926,1511904145810915449

1
.gitignore vendored
View file

@ -13,6 +13,7 @@ data/wrank.json
data/bringer.json data/bringer.json
data/sessionPreferences.json data/sessionPreferences.json
data/tg-history/ data/tg-history/
data/poll-state.json
# Emoji data # Emoji data
emoji-uploads/ emoji-uploads/

0
data/.gitkeep Normal file
View file

View file

@ -1,113 +0,0 @@
{
"flash": {
"characters": [
{
"name": "«Flash»",
"class": "FB",
"level": 79,
"nation": "Procyon",
"active": false,
"sharedWith": [
"invicjusz"
]
},
{
"name": "»Flash«",
"class": "WI",
"level": 79,
"nation": "Procyon",
"active": true,
"sharedWith": [
"invicjusz"
]
}
]
},
"zephyr": {
"characters": [
{
"name": "XefronYokuda",
"class": "FA",
"level": 79,
"nation": "Capella",
"active": true
}
]
},
"dey": {
"characters": [
{
"name": "«Deystroyer»",
"class": "BL",
"level": 79,
"nation": "Capella",
"active": true,
"sharedWith": [
"flash"
]
}
]
},
"keira": {
"characters": [
{
"name": "«Keira»",
"class": "WI",
"level": 79,
"nation": "Capella",
"active": true
}
]
},
"ayana": {
"characters": [
{
"name": "«MonkeyHunter»",
"class": "DM",
"level": 79,
"nation": "Procyon",
"active": true
}
]
},
"invicjusz": {
"characters": [
{
"name": "ElementalEnchant",
"class": "FB",
"level": 76,
"nation": "Procyon",
"active": true
}
]
},
"marin": {
"characters": [
{
"name": "iMarieLaveau",
"class": "DM",
"level": 79,
"nation": "Capella",
"active": true
}
]
},
"sean": {
"characters": [
{
"name": "»No.1«",
"class": "FB",
"level": 79,
"nation": "Capella",
"active": true
},
{
"name": "«No.1»",
"class": "GL",
"level": 79,
"nation": "Capella",
"active": false
}
]
}
}

View file

@ -1,4 +1,5 @@
{ {
"showLevelInMessages": true, "showLevelInMessages": true,
"showClassInMessages": true "showClassInMessages": true,
"confirmYesMessage": "⚔️ TG is confirmed for tonight!"
} }

View file

@ -1,26 +0,0 @@
[
{
"messageId": "1511906795667456040",
"slot": 20,
"yes": [
[
"164487045052497920",
{
"userKey": "flash",
"displayName": "flash",
"characterName": "»Flash«",
"characterClass": "WI",
"characterLevel": 79,
"characterNation": "Procyon",
"discordId": "164487045052497920",
"votedAt": "03:53",
"previousNoAt": "03:53",
"publicMessage": "Flash? Flash? Flash!!"
}
]
],
"no": [],
"locked": false,
"confirmed": null
}
]

View file

@ -1,29 +0,0 @@
{
"slot": 0,
"date": "2026-06-01",
"confirmed": false,
"nationKD": {
"source": "Procyon",
"capella": {
"k": 0,
"d": 0
},
"procyon": {
"k": 0,
"d": 0
}
},
"scores": [
{
"usermapKey": "flash",
"characterName": "»Flash«",
"class": "WI",
"nation": "Procyon",
"pts": 2000,
"submittedAt": "2026-06-01T03:22:25.475Z",
"slot": 0,
"date": "2026-06-01",
"submittedByOfficer": false
}
]
}

View file

@ -1,29 +0,0 @@
{
"slot": 2,
"date": "2026-06-01",
"confirmed": false,
"nationKD": {
"source": "Procyon",
"capella": {
"k": 0,
"d": 0
},
"procyon": {
"k": 0,
"d": 0
}
},
"scores": [
{
"usermapKey": "flash",
"characterName": "»Flash«",
"class": "WI",
"nation": "Procyon",
"pts": 100,
"submittedAt": "2026-06-01T03:22:43.115Z",
"slot": 2,
"date": "2026-06-01",
"submittedByOfficer": false
}
]
}

View file

@ -1,29 +0,0 @@
{
"slot": 4,
"date": "2026-06-01",
"confirmed": false,
"nationKD": {
"source": "Procyon",
"capella": {
"k": 0,
"d": 0
},
"procyon": {
"k": 0,
"d": 0
}
},
"scores": [
{
"usermapKey": "flash",
"characterName": "»Flash«",
"class": "WI",
"nation": "Procyon",
"pts": 100,
"submittedAt": "2026-06-01T03:22:48.373Z",
"slot": 4,
"date": "2026-06-01",
"submittedByOfficer": false
}
]
}

View file

@ -1,29 +0,0 @@
{
"slot": 6,
"date": "2026-06-01",
"confirmed": false,
"nationKD": {
"source": "Procyon",
"capella": {
"k": 0,
"d": 0
},
"procyon": {
"k": 0,
"d": 0
}
},
"scores": [
{
"usermapKey": "flash",
"characterName": "»Flash«",
"class": "WI",
"nation": "Procyon",
"pts": 100,
"submittedAt": "2026-06-01T03:22:54.521Z",
"slot": 6,
"date": "2026-06-01",
"submittedByOfficer": false
}
]
}

View file

@ -1,29 +0,0 @@
{
"slot": 8,
"date": "2026-06-01",
"confirmed": false,
"nationKD": {
"source": "Procyon",
"capella": {
"k": 0,
"d": 0
},
"procyon": {
"k": 0,
"d": 0
}
},
"scores": [
{
"usermapKey": "flash",
"characterName": "»Flash«",
"class": "WI",
"nation": "Procyon",
"pts": 100,
"submittedAt": "2026-06-01T03:23:03.650Z",
"slot": 8,
"date": "2026-06-01",
"submittedByOfficer": false
}
]
}

View file

@ -1,40 +0,0 @@
{
"slot": 20,
"date": "2026-06-01",
"confirmed": false,
"nationKD": {
"source": "Procyon",
"capella": {
"k": 0,
"d": 0
},
"procyon": {
"k": 0,
"d": 0
}
},
"scores": [
{
"usermapKey": "invicjusz",
"characterName": "ElementalEnchant",
"class": "FB",
"nation": "Procyon",
"pts": 5000,
"submittedAt": "2026-06-01T03:19:12.073Z",
"slot": 20,
"date": "2026-06-01",
"submittedByOfficer": true
},
{
"usermapKey": "flash",
"characterName": "«Flash»",
"class": "FB",
"nation": "Procyon",
"pts": 2000,
"submittedAt": "2026-06-01T22:07:39.907Z",
"slot": 20,
"date": "2026-06-01",
"submittedByOfficer": true
}
]
}

View file

@ -1,29 +0,0 @@
{
"slot": 22,
"date": "2026-06-01",
"confirmed": false,
"nationKD": {
"source": "Procyon",
"capella": {
"k": 0,
"d": 0
},
"procyon": {
"k": 0,
"d": 0
}
},
"scores": [
{
"usermapKey": "flash",
"characterName": "»Flash«",
"class": "WI",
"nation": "Procyon",
"pts": 1000,
"submittedAt": "2026-06-01T22:05:28.186Z",
"slot": 22,
"date": "2026-06-01",
"submittedByOfficer": false
}
]
}

View file

@ -7,7 +7,8 @@
"invicjusz": "invicjusz", "invicjusz": "invicjusz",
"mrsean.": "sean", "mrsean.": "sean",
"ibenni": "ayana", "ibenni": "ayana",
"zephyr_74135": "zephyr", "izephyrxy": "zephyr",
"eat.jim.sleep": "keira", "eat.jim.sleep": "keira",
"mar1n1987": "marin" "mar1n1987": "marin",
"coba5539": "cobain"
} }

View file

@ -1,114 +0,0 @@
{
"2026-W23": {
"weekKey": "2026-W23",
"entries": {
"capella": [
{
"userKey": "zephyr",
"characterName": "XefronYokuda",
"class": "FA",
"nation": "Capella",
"weeklyPoints": 1415,
"tgCount": 2,
"currentRank": 4,
"previousRank": 3
},
{
"userKey": "dey",
"characterName": "«Deystroyer»",
"class": "BL",
"nation": "Capella",
"weeklyPoints": 3640,
"tgCount": 2,
"currentRank": 2,
"previousRank": 1
},
{
"userKey": "keira",
"characterName": "«Keira»",
"class": "WI",
"nation": "Capella",
"weeklyPoints": 4000,
"tgCount": 1,
"currentRank": 1,
"previousRank": 2
},
{
"userKey": "sean",
"characterName": "»No.1«",
"class": "FB",
"nation": "Capella",
"weeklyPoints": 1666,
"tgCount": 1,
"currentRank": 3
}
],
"procyon": [
{
"userKey": "flash",
"characterName": "»Flash«",
"class": "WI",
"nation": "Procyon",
"weeklyPoints": 5179,
"tgCount": 7,
"currentRank": 1,
"previousRank": 2
},
{
"userKey": "invicjusz",
"characterName": "ElementalEnchant",
"class": "FB",
"nation": "Procyon",
"weeklyPoints": 2503,
"tgCount": 2,
"currentRank": 3,
"previousRank": 3
},
{
"userKey": "ayana",
"characterName": "«MonkeyHunter»",
"class": "DM",
"nation": "Procyon",
"weeklyPoints": 4741,
"tgCount": 2,
"currentRank": 2,
"previousRank": 1
}
]
},
"scoreIndex": {
"flash": [
"2026-06-01-20",
"2026-06-02-20"
],
"invicjusz": [
"2026-06-01-20",
"2026-06-02-20"
],
"ayana": [
"2026-06-01-20",
"2026-06-02-20"
],
"zephyr": [
"2026-06-01-20",
"2026-06-02-20"
],
"dey": [
"2026-06-01-20",
"2026-06-02-20"
],
"keira": [
"2026-06-02-20"
],
"sean": [
"2026-06-02-20"
]
},
"bringer": {
"capella": null,
"procyon": null,
"procyonOverride": "»Flash«",
"capellaOverride": "XefronYokuda"
}
}
}

View file

@ -1,17 +1,17 @@
services: services:
tg-bot-dev: tg-bot:
build: build:
context: /opt/docker/tg-bot-ts-dev context: /opt/docker/tg-bot-ts
image: tg-bot-ts-dev:latest image: tg-bot-ts:latest
container_name: tg-bot-ts-dev container_name: tg-bot-ts
restart: unless-stopped restart: unless-stopped
env_file: env_file:
- /opt/docker/tg-bot-ts-dev/.env - /opt/docker/tg-bot-ts/.env
volumes: volumes:
- /opt/docker/tg-bot-ts-dev/src:/app/src - /opt/docker/tg-bot-ts/src:/app/src
- /opt/docker/tg-bot-ts-dev/data:/app/data - /opt/docker/tg-bot-ts/data:/app/data
- /opt/docker/tg-bot-ts-dev/scripts:/app/scripts - /opt/docker/tg-bot-ts-dev/scripts:/app/scripts
- /opt/docker/tg-bot-ts-dev/messages:/app/messages - /opt/docker/tg-bot-ts/messages:/app/messages
- /opt/docker/tg-bot-ts-dev/emoji-uploads:/app/emoji-uploads - /opt/docker/tg-bot-ts-dev/emoji-uploads:/app/emoji-uploads
- /opt/docker/tg-bot-ts-dev/tsconfig.json:/app/tsconfig.json - /opt/docker/tg-bot-ts-dev/tsconfig.json:/app/tsconfig.json
- /opt/docker/tg-bot-ts-dev/data/sessionPreferences.json:/app/data/sessionPreferences.json - /opt/docker/tg-bot-ts-dev/data/sessionPreferences.json:/app/data/sessionPreferences.json

View file

@ -16,26 +16,91 @@
"storm_bringer": "<:storm_bringer:1511906496097554594>", "storm_bringer": "<:storm_bringer:1511906496097554594>",
"wa": "<:wa:1511906499889467492>", "wa": "<:wa:1511906499889467492>",
"wi": "<:wi:1511906503647563807>", "wi": "<:wi:1511906503647563807>",
"wrank_1": "<:wrank_1:1511906507485085736>", "wrank_1": "<:wrank_1:1512124887592996995>",
"wrank_1_gold": "<:wrank_1_gold:1511906510806978742>", "wrank_1_gold": "<:wrank_1_gold:1512125051728560278>",
"wrank_2": "<:wrank_2:1511906514745430217>", "wrank_2": "<:wrank_2:1512124931075342376>",
"wrank_2_gold": "<:wrank_2_gold:1511906518386212864>", "wrank_2_gold": "<:wrank_2_gold:1512125095974535271>",
"wrank_3": "<:wrank_3:1511906522265944154>", "wrank_3": "<:wrank_3:1512124938453254334>",
"wrank_3_gold": "<:wrank_3_gold:1511906526204530690>", "wrank_3_gold": "<:wrank_3_gold:1512125103964684390>",
"wrank_4": "<:wrank_4:1511906530692173915>", "wrank_4": "<:wrank_4:1512124943465316433>",
"wrank_4_gold": "<:wrank_4_gold:1511906534790266883>", "wrank_4_gold": "<:wrank_4_gold:1512125108154663133>",
"wrank_5": "<:wrank_5:1511906539223388322>", "wrank_5": "<:wrank_5:1512124947852431513>",
"wrank_5_gold": "<:wrank_5_gold:1511906543342452837>", "wrank_5_gold": "<:wrank_5_gold:1512125112084594818>",
"wrank_down": "<:wrank_down:1511906547104616643>", "wrank_down": "<:wrank_down:1511906547104616643>",
"wrank_down_1": "<:wrank_down_1:1511906550698999909>", "wrank_down_1": "<:wrank_down_1:1512124970698801244>",
"wrank_down_2": "<:wrank_down_2:1511906554507694120>", "wrank_down_2": "<:wrank_down_2:1512125016114729133>",
"wrank_down_3": "<:wrank_down_3:1511906558231969792>", "wrank_down_3": "<:wrank_down_3:1512125023199166536>",
"wrank_down_4": "<:wrank_down_4:1511906562011304007>", "wrank_down_4": "<:wrank_down_4:1512125027372237040>",
"wrank_down_5": "<:wrank_down_5:1511906565630984273>", "wrank_down_5": "<:wrank_down_5:1512125030765691072>",
"wrank_up": "<:wrank_up:1511906568877117576>", "wrank_neutral": "<:wrank_neutral:1511950713713070160>",
"wrank_up_1": "<:wrank_up_1:1511906573537120287>", "wrank_neutral_0": "<:wrank_neutral_0:1511950717290545354>",
"wrank_up_2": "<:wrank_up_2:1511906577970364536>", "wrank_no_dash": "<:wrank_no_dash:1511956379403943979>",
"wrank_up_3": "<:wrank_up_3:1511906581711945909>", "wrank_up_1": "<:wrank_up_1:1512125132242554890>",
"wrank_up_4": "<:wrank_up_4:1511906585503338616>", "wrank_up_10": "<:wrank_up_10:1512125136445243503>",
"wrank_up_5": "<:wrank_up_5:1511906588921954325>" "wrank_up_2": "<:wrank_up_2:1512127569259135139>",
"wrank_up_3": "<:wrank_up_3:1512127577051893843>",
"wrank_up_4": "<:wrank_up_4:1512127582068281455>",
"wrank_up_6": "<:wrank_up_6:1512127590062620723>",
"wrank_up_7": "<:wrank_up_7:1512127593883766978>",
"wrank_up_8": "<:wrank_up_8:1512127598044643428>",
"wrank_up_9": "<:wrank_up_9:1512127601811128501>",
"wrank_up": "<:wrank_up:1512114414474756132>",
"wrank_up_5": "<:wrank_up_5:1512127585826377928>",
"wrank_up_11": "<:wrank_up_11:1512125140454998018>",
"wrank_up_12": "<:wrank_up_12:1512125144984719630>",
"wrank_up_13": "<:wrank_up_13:1512125149057388667>",
"wrank_up_14": "<:wrank_up_14:1512125153671123124>",
"wrank_up_15": "<:wrank_up_15:1512127541995901100>",
"wrank_up_16": "<:wrank_up_16:1512127545753993439>",
"wrank_up_17": "<:wrank_up_17:1512127549956821002>",
"wrank_up_18": "<:wrank_up_18:1512127553995931959>",
"wrank_up_19": "<:wrank_up_19:1512127558143971534>",
"wrank_up_20": "<:wrank_up_20:1512127573092597840>",
"wrank_10": "<:wrank_10:1512124891250561096>",
"wrank_11": "<:wrank_11:1512124894694080576>",
"wrank_12": "<:wrank_12:1512124898611429387>",
"wrank_13": "<:wrank_13:1512124902831030282>",
"wrank_14": "<:wrank_14:1512124907511611537>",
"wrank_15": "<:wrank_15:1512124911550730452>",
"wrank_16": "<:wrank_16:1512124915367673886>",
"wrank_17": "<:wrank_17:1512124919029305434>",
"wrank_18": "<:wrank_18:1512124923018219721>",
"wrank_19": "<:wrank_19:1512124927262855239>",
"wrank_20": "<:wrank_20:1512124934762135684>",
"wrank_6": "<:wrank_6:1512124952738795581>",
"wrank_7": "<:wrank_7:1512124956622979143>",
"wrank_8": "<:wrank_8:1512124961450496020>",
"wrank_9": "<:wrank_9:1512124965363650631>",
"wrank_down_10": "<:wrank_down_10:1512124974582989000>",
"wrank_down_11": "<:wrank_down_11:1512124978504536114>",
"wrank_down_12": "<:wrank_down_12:1512124982728069192>",
"wrank_down_13": "<:wrank_down_13:1512124987501314150>",
"wrank_down_14": "<:wrank_down_14:1512124991292837918>",
"wrank_down_15": "<:wrank_down_15:1512124995340468335>",
"wrank_down_16": "<:wrank_down_16:1512124999576850462>",
"wrank_down_17": "<:wrank_down_17:1512125004353896642>",
"wrank_down_18": "<:wrank_down_18:1512125008132964486>",
"wrank_down_19": "<:wrank_down_19:1512125011857510410>",
"wrank_down_20": "<:wrank_down_20:1512125019814101173>",
"wrank_down_6": "<:wrank_down_6:1512125035123576922>",
"wrank_down_7": "<:wrank_down_7:1512125039091126434>",
"wrank_down_8": "<:wrank_down_8:1512125042757210123>",
"wrank_down_9": "<:wrank_down_9:1512125047798759706>",
"wrank_10_gold": "<:wrank_10_gold:1512125055432396910>",
"wrank_11_gold": "<:wrank_11_gold:1512125059123249242>",
"wrank_12_gold": "<:wrank_12_gold:1512125063304974438>",
"wrank_13_gold": "<:wrank_13_gold:1512125067201609908>",
"wrank_14_gold": "<:wrank_14_gold:1512125071043596520>",
"wrank_15_gold": "<:wrank_15_gold:1512125074893832443>",
"wrank_16_gold": "<:wrank_16_gold:1512125078966374420>",
"wrank_17_gold": "<:wrank_17_gold:1512125083605532715>",
"wrank_18_gold": "<:wrank_18_gold:1512125088378392718>",
"wrank_19_gold": "<:wrank_19_gold:1512125091960459508>",
"wrank_20_gold": "<:wrank_20_gold:1512125100265181204>",
"wrank_6_gold": "<:wrank_6_gold:1512125115956203601>",
"wrank_7_gold": "<:wrank_7_gold:1512125120204771338>",
"wrank_8_gold": "<:wrank_8_gold:1512125123874918661>",
"wrank_9_gold": "<:wrank_9_gold:1512125128299905104>",
"wrank_no_rank": "<:wrank_no_rank:1512261782205628606>",
"wrank_no_rank_delta": "<:wrank_no_rank_delta:1512263603519229982>"
} }

View file

@ -1,13 +1,16 @@
{ {
"public": { "public": {
"yes": [ "yes": [
{ "clicks": 1, "random": true, "messages": ["Ayana is in"]}, { "clicks": 1, "random": true, "messages": ["HELP, KURWA! I survive, easy!", "Send emergency services!", "Aiaiaiaiai I'm unmoved, HELP!", "You want beef?! Let's go!", "I don't give a fuuuuuck", "Ayana is in"]},
{ "clicks": 10, "random": true, "messages": ["Ayana..."] } { "clicks": 10, "random": true, "messages": ["Ayana..."] }
], ],
"no": [ "no": [
{ "clicks": 1, "random": true, "messages": [ { "clicks": 1, "random": true, "messages": [
"Went for a kebab", "Went for a kebab",
"Doesn't give a fuck" "Doesn't give a fuck",
"Went outside... for a change",
"Is touching grass",
"Is unavailable, call your local DM hotline"
] ]
} }
] ]

View file

@ -1,15 +1,21 @@
{ {
"public": { "public": {
"yes": [ "yes": [
{ "clicks": 1, "random": true, "messages": ["Dey is in"]}, { "clicks": 1, "random": true, "messages": [
{ "clicks": 2, "random": true, "messages": ["Courageous now, new account afterall"] }, "Dey is in",
"Dey is in... for now",
"Welcome indeed!",
"A bit of this, a bit of that.",
"Come and see what goods I offer"
]},
{ "clicks": 10, "random": true, "messages": ["Now you're just asking for it."] } { "clicks": 10, "random": true, "messages": ["Now you're just asking for it."] }
], ],
"no": [ "no": [
{ "clicks": 1, "random": true, "messages": [ { "clicks": 1, "random": true, "messages": [
"Everything's for sale", "Everything's for sale",
"Dey roaching out 🪳", "Dey roaching out 🪳",
"Dey said no... shocking" "Dey said no... shocking",
"No more... I yield!"
] ]
} }
] ]

View file

@ -4,9 +4,11 @@
{ {
"clicks": 1, "clicks": 1,
"random": true, "random": true,
"messages": ["The King has arrived. 👑", "Flash is in, bow down.", "👑 Royalty has entered the raid.","{alias[0]} is in"] "messages": [
}, "<:wi:1511906503647563807>+<:storm_bringer:1511906496097554594>=<:kd:1511906474497146983>",
{ "clicks": 2, "random": true, "messages": ["Flash? Flash? Flash!!"] } "<:wi:1511906503647563807> Powaaaaaaaaa"
]
}
], ],
"no": [ "no": [
{ "clicks": 1, "random": true, "messages": [ { "clicks": 1, "random": true, "messages": [

View file

@ -2,7 +2,7 @@
"public": { "public": {
"yes": [ "yes": [
{ "clicks": 1, "random": true, "messages": [ { "clicks": 1, "random": true, "messages": [
"Vic is in" "Vic is in", "Inviiiiiiiiiiiicjusz"
] ]
}, },
{ "clicks": 2, "random": true, "messages": ["Vic is really in"] }, { "clicks": 2, "random": true, "messages": ["Vic is really in"] },

View file

@ -5,7 +5,8 @@
"Legend is in", "Legend is in",
"Best FA shows up", "Best FA shows up",
"Healmeister reporting for duty", "Healmeister reporting for duty",
"Capella MVP is up" "Capella MVP is up",
"A wild Zephyr appears!"
] ]
} }
], ],

View file

@ -49,15 +49,6 @@ export async function handleRankPost(interaction: ChatInputCommandInteraction):
) )
.setTimestamp(); .setTimestamp();
// const embed = new EmbedBuilder()
// .setTitle(`⚔️ W.Rank Leaderboard — ${weekKey}`)
// .setColor(0xe8a317)
// .addFields(
// { name: "🔵 Capella", value: formatNation("capella"), inline: true },
// { name: "🔴 Procyon", value: formatNation("procyon"), inline: true },
// )
// .setTimestamp();
const channelId = cfg("resultsChannelId") || cfg("pollChannelId"); const channelId = cfg("resultsChannelId") || cfg("pollChannelId");
const channel = await interaction.client.channels.fetch(channelId) as TextChannel; const channel = await interaction.client.channels.fetch(channelId) as TextChannel;
await channel.send({ embeds: [embed] }); await channel.send({ embeds: [embed] });

View file

@ -92,7 +92,7 @@ function wrankDelta(entry: WRankEntry, options?: { brackets?: boolean }): string
const numEmoji = getEmoji(`wrank_down_${delta}`); const numEmoji = getEmoji(`wrank_down_${delta}`);
inner = (getEmoji("wrank_down") || "↓") + (numEmoji || delta); inner = (getEmoji("wrank_down") || "↓") + (numEmoji || delta);
} else { } else {
inner = (getEmoji("wrank_neutral") || "·") + "0"; inner = (getEmoji("wrank_no_dash") || "·") + (getEmoji("wrank_neutral_0") || "0");
} }
return brackets ? ` (${inner})` : ` ${inner}`; return brackets ? ` (${inner})` : ` ${inner}`;
@ -106,6 +106,18 @@ function wrankFull(entry: WRankEntry, options: WRankDisplayOptions): string {
return wrankRank(entry, options.goal) + wrankDelta(entry, { brackets: options.brackets }); return wrankRank(entry, options.goal) + wrankDelta(entry, { brackets: options.brackets });
} }
/**
* Placeholder for characters with no W.Rank when others in their nation have one.
* Output: ( [] )
*/
function wrankNoRank(): string {
const norank = getEmoji("wrank_no_dash") || "—";
const dash = getEmoji("wrank_no_rank_delta") || "—";
const square = getEmoji("wrank_no_dash") || "■";
return `${norank} (${square}${dash})`;
}
// ─── Namespace export ───────────────────────────────────────────────────────── // ─── Namespace export ─────────────────────────────────────────────────────────
export const format = { export const format = {
@ -117,5 +129,6 @@ export const format = {
rank: wrankRank, rank: wrankRank,
delta: wrankDelta, delta: wrankDelta,
full: wrankFull, full: wrankFull,
noRank: wrankNoRank,
}, },
}; };

View file

@ -42,7 +42,7 @@ export function upsertScore(score: TGScore): void {
// Overwrite existing score for this player+slot // Overwrite existing score for this player+slot
result.scores = result.scores.filter( result.scores = result.scores.filter(
(s) => !(s.userKey === score.userKey && s.slot === score.slot && s.date === score.date) (s) => !(s.userKey === score.userKey && s.characterName === score.characterName && s.slot === score.slot && s.date === score.date)
); );
result.scores.push(score); result.scores.push(score);
saveResult(result); saveResult(result);

View file

@ -72,15 +72,42 @@ export function lockPoll(slot: number): void {
// ─── Character display ──────────────────────────────────────────────────────── // ─── Character display ────────────────────────────────────────────────────────
function formatCharRow(entry: VoteEntry, showNationEmoji = false): string { function getNationBringerTitle(nation: Nation) {
const stormBringerIcon = getEmoji("storm_bringer") || "⚡";
const stormBringer = `${stormBringerIcon}`;
const luminousBringerIcon = getEmoji("luminous_bringer") || "🔆";
const luminousBringer = `${luminousBringerIcon}` || `🔆 Luminous Bringer`;
const nationMap = {
"Capella": luminousBringer,
"Procyon": stormBringer
};
return nationMap[nation];
}
function getBringerDisplay(nation: Nation): string {
const bringerMap: Record<Nation, string> = {
Capella: getEmoji("luminous_bringer") || "🔆 Luminous Bringer",
Procyon: getEmoji("storm_bringer") || "⚡ Storm Bringer",
};
return bringerMap[nation];
}
function formatCharRow(entry: VoteEntry, showNationEmoji = false, nationHasRank = false): string {
const cfgFormat = cfg("charDisplayFormat"); const cfgFormat = cfg("charDisplayFormat");
const nation = entry.characterNation; const nation = entry.characterNation;
const wRankEntry = entry.characterName ? getEntry(entry.characterName, nation ?? "Capella") : null; const wRankEntry = entry.characterName && entry.characterNation
? getEntry(entry.characterName, entry.characterNation)
: null;
let wrank = ""; let wrank = "";
if (wRankEntry) { if (wRankEntry) {
const wRankGoal = cfg("wRankGoal"); const wRankGoal = cfg("wRankGoal");
wrank = format.wrank.full(wRankEntry, { goal: wRankGoal, brackets: true }); wrank = format.wrank.full(wRankEntry, { goal: wRankGoal, brackets: true });
} else if (nationHasRank) {
wrank = format.wrank.noRank();
} }
const classStr = entry.characterClass const classStr = entry.characterClass
@ -100,33 +127,11 @@ function formatCharRow(entry: VoteEntry, showNationEmoji = false): string {
.trim(); .trim();
// Bringer title — independent of W.Rank so override always shows // Bringer title — independent of W.Rank so override always shows
function getNationBringerTitle(nation: Nation) {
const stormBringerIcon = getEmoji("storm_bringer") || "⚡";
const stormBringer = `${stormBringerIcon}`;
const luminousBringerIcon = getEmoji("luminous_bringer") || "⚡";
const luminousBringer = `${luminousBringerIcon}` || `⚡ Luminous Bringer`;
const nationMap = {
"Capella": luminousBringer,
"Procyon": stormBringer
};
return nationMap[nation];
}
if (nation && entry.userKey) { if (nation && entry.userKey) {
const bringer = getBringer(nation); const bringer = getBringer(nation);
if (bringer && bringer === entry.characterName) { if (bringer && bringer === entry.characterName) {
const bringerTitle = getNationBringerTitle(nation); row += ` · ${getBringerDisplay(nation)}`;
row += ` · ${bringerTitle}`;
} }
// if (bringer && bringer === entry.characterName) {
// const emoji = nation === "Capella"
// ? (getEmoji("luminous_bringer") || "🔆")
// : (getEmoji("storm_bringer") || "⚡");
// const title = nation === "Capella" ? "Luminous Bringer" : "Storm Bringer";
// row += ` · ${emoji} **${title}**`;
// }
} }
if (entry.borrowedFrom) { if (entry.borrowedFrom) {
@ -160,12 +165,13 @@ export function buildEmbed(state: PollState, overrideLockMsg?: string): EmbedBui
const formatNationField = (nation: Nation): string => { const formatNationField = (nation: Nation): string => {
const yesEntries = yesByNation[nation]; const yesEntries = yesByNation[nation];
const hasRank = yesEntries.some((e) => e.characterName && getEntry(e.characterName, nation) !== null);
const noEntries = showNoInline const noEntries = showNoInline
? noVoters.filter((e) => e.characterNation === nation) ? noVoters.filter((e) => e.characterNation === nation)
: []; : [];
const lines = [ const lines = [
...yesEntries.map((e) => formatCharRow(e)), ...yesEntries.map((e) => formatCharRow(e, false, hasRank)),
...noEntries.map((e) => `${formatCharRow(e)}`), ...noEntries.map((e) => `${formatCharRow(e, false, hasRank)}`),
]; ];
return lines.length > 0 ? lines.join("\n") : "—"; return lines.length > 0 ? lines.join("\n") : "—";
}; };

View file

@ -99,12 +99,16 @@ export function recordScore(
} }
function recomputeRanks(week: WRankWeek, nation: Nation): void { function recomputeRanks(week: WRankWeek, nation: Nation): void {
const list = week.entries[nation.toLowerCase() as "capella" | "procyon"]; const list = week.entries[nation.toLowerCase() as "capella" | "procyon"];
const sorted = [...list].sort((a, b) => b.weeklyPoints - a.weeklyPoints); const sorted = [...list].sort((a, b) => b.weeklyPoints - a.weeklyPoints);
sorted.forEach((entry, i) => { sorted.forEach((entry, i) => {
const live = list.find((e) => e.characterName === entry.characterName)!; const live = list.find((e) => e.characterName === entry.characterName)!;
live.previousRank = live.currentRank || undefined; const newRank = i + 1;
live.currentRank = i + 1; // Only snapshot previousRank when rank actually changes
if (live.currentRank !== 0 && live.currentRank !== newRank) {
live.previousRank = live.currentRank;
}
live.currentRank = newRank;
}); });
} }
@ -146,6 +150,8 @@ export function getBringer(nation: Nation): string | null {
export function getEntry(characterName: string, nation: Nation): WRankEntry | null { export function getEntry(characterName: string, nation: Nation): WRankEntry | null {
const week = getCurrentWeek(); const week = getCurrentWeek();
const list = week.entries[nation.toLowerCase() as "capella" | "procyon"]; const list = week.entries[nation.toLowerCase() as "capella" | "procyon"];
console.log(`[getEntry] weekKey=${week.weekKey} nation=${nation} listLength=${list?.length} looking for=${characterName}`);
console.log(`[getEntry] available:`, list?.map(e => e.characterName));
return list.find((e) => e.characterName === characterName) ?? null; return list.find((e) => e.characterName === characterName) ?? null;
} }

View file

@ -14,6 +14,7 @@
"paths": { "paths": {
"@src/*": ["src/*"], "@src/*": ["src/*"],
"@data/*": ["data/*"], "@data/*": ["data/*"],
"@tests/*": ["tests/*"],
"@messages/*": ["messages/*"], "@messages/*": ["messages/*"],
"@tgHistory/*": ["data/tg-history/*"], "@tgHistory/*": ["data/tg-history/*"],
"@scripts/*": ["scripts/*"], "@scripts/*": ["scripts/*"],