limit by UID and server (#1251)
This commit is contained in:
+55
-10
@@ -127,8 +127,10 @@ from .utils import (
|
|||||||
LocaleJsonTranslator,
|
LocaleJsonTranslator,
|
||||||
COMP_FREE_UNTIL_TS,
|
COMP_FREE_UNTIL_TS,
|
||||||
COMP_LIMIT_PER_TIMESLOT,
|
COMP_LIMIT_PER_TIMESLOT,
|
||||||
|
COMP_LIMIT_PER_USER_PER_TIMESLOT,
|
||||||
get_current_timeslot_start_ts,
|
get_current_timeslot_start_ts,
|
||||||
get_comp_usage_in_timeslot,
|
get_comp_usage_in_timeslot,
|
||||||
|
get_comp_usage_in_timeslot_by_user,
|
||||||
SQB_SLOTS_POSTED,
|
SQB_SLOTS_POSTED,
|
||||||
RECAP_LANGS,
|
RECAP_LANGS,
|
||||||
RecapError,
|
RecapError,
|
||||||
@@ -403,24 +405,57 @@ async def comp(interaction: discord.Interaction, squadron_short: str):
|
|||||||
if slot_start is not None:
|
if slot_start is not None:
|
||||||
entitled = await is_guild_entitled(guild.id)
|
entitled = await is_guild_entitled(guild.id)
|
||||||
if not entitled:
|
if not entitled:
|
||||||
used = await get_comp_usage_in_timeslot(guild.id, slot_start)
|
used_server = await get_comp_usage_in_timeslot(guild.id, slot_start)
|
||||||
|
used_user = await get_comp_usage_in_timeslot_by_user(
|
||||||
|
guild.id, interaction.user.id, slot_start
|
||||||
|
)
|
||||||
logging.info(
|
logging.info(
|
||||||
"[COMP-USAGE] guild=%s user=%s squadron=%s used=%s limit=%s slot_start=%s entitled=%s",
|
"[COMP-USAGE] guild=%s user=%s squadron=%s used_server=%s/%s used_user=%s/%s slot_start=%s entitled=%s",
|
||||||
guild.id,
|
guild.id,
|
||||||
interaction.user.id,
|
interaction.user.id,
|
||||||
squadron_short,
|
squadron_short,
|
||||||
used,
|
used_server,
|
||||||
COMP_LIMIT_PER_TIMESLOT,
|
COMP_LIMIT_PER_TIMESLOT,
|
||||||
|
used_user,
|
||||||
|
COMP_LIMIT_PER_USER_PER_TIMESLOT,
|
||||||
slot_start,
|
slot_start,
|
||||||
entitled,
|
entitled,
|
||||||
)
|
)
|
||||||
if used >= COMP_LIMIT_PER_TIMESLOT:
|
if used_user >= COMP_LIMIT_PER_USER_PER_TIMESLOT:
|
||||||
logging.warning(
|
logging.warning(
|
||||||
"[COMP-LIMIT] guild=%s user=%s squadron=%s used=%s limit=%s slot_start=%s action=blocked",
|
"[COMP-LIMIT] guild=%s user=%s squadron=%s scope=user used_user=%s/%s slot_start=%s action=blocked",
|
||||||
guild.id,
|
guild.id,
|
||||||
interaction.user.id,
|
interaction.user.id,
|
||||||
squadron_short,
|
squadron_short,
|
||||||
used,
|
used_user,
|
||||||
|
COMP_LIMIT_PER_USER_PER_TIMESLOT,
|
||||||
|
slot_start,
|
||||||
|
)
|
||||||
|
embed_limit = discord.Embed(
|
||||||
|
title=t(lang, "comp.limit_reached_title"),
|
||||||
|
description=t(
|
||||||
|
lang,
|
||||||
|
"comp.user_limit_reached_desc",
|
||||||
|
limit=COMP_LIMIT_PER_USER_PER_TIMESLOT,
|
||||||
|
),
|
||||||
|
color=discord.Color.red()
|
||||||
|
)
|
||||||
|
embed_limit.set_footer(
|
||||||
|
text=t(
|
||||||
|
lang,
|
||||||
|
"comp.user_remaining_footer",
|
||||||
|
remaining=0,
|
||||||
|
limit=COMP_LIMIT_PER_USER_PER_TIMESLOT,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return await interaction.followup.send(embed=embed_limit)
|
||||||
|
if used_server >= COMP_LIMIT_PER_TIMESLOT:
|
||||||
|
logging.warning(
|
||||||
|
"[COMP-LIMIT] guild=%s user=%s squadron=%s scope=server used_server=%s/%s slot_start=%s action=blocked",
|
||||||
|
guild.id,
|
||||||
|
interaction.user.id,
|
||||||
|
squadron_short,
|
||||||
|
used_server,
|
||||||
COMP_LIMIT_PER_TIMESLOT,
|
COMP_LIMIT_PER_TIMESLOT,
|
||||||
slot_start,
|
slot_start,
|
||||||
)
|
)
|
||||||
@@ -431,17 +466,27 @@ async def comp(interaction: discord.Interaction, squadron_short: str):
|
|||||||
)
|
)
|
||||||
embed_limit.set_footer(text=t(lang, "comp.remaining_footer", remaining=0, limit=COMP_LIMIT_PER_TIMESLOT))
|
embed_limit.set_footer(text=t(lang, "comp.remaining_footer", remaining=0, limit=COMP_LIMIT_PER_TIMESLOT))
|
||||||
return await interaction.followup.send(embed=embed_limit)
|
return await interaction.followup.send(embed=embed_limit)
|
||||||
remaining = COMP_LIMIT_PER_TIMESLOT - used - 1
|
user_remaining = COMP_LIMIT_PER_USER_PER_TIMESLOT - used_user - 1
|
||||||
|
server_remaining = COMP_LIMIT_PER_TIMESLOT - used_server - 1
|
||||||
logging.info(
|
logging.info(
|
||||||
"[COMP-REMAINING] guild=%s user=%s squadron=%s remaining=%s limit=%s slot_start=%s",
|
"[COMP-REMAINING] guild=%s user=%s squadron=%s user_remaining=%s/%s server_remaining=%s/%s slot_start=%s",
|
||||||
guild.id,
|
guild.id,
|
||||||
interaction.user.id,
|
interaction.user.id,
|
||||||
squadron_short,
|
squadron_short,
|
||||||
remaining,
|
user_remaining,
|
||||||
|
COMP_LIMIT_PER_USER_PER_TIMESLOT,
|
||||||
|
server_remaining,
|
||||||
COMP_LIMIT_PER_TIMESLOT,
|
COMP_LIMIT_PER_TIMESLOT,
|
||||||
slot_start,
|
slot_start,
|
||||||
)
|
)
|
||||||
comp_footer = t(lang, "comp.remaining_footer", remaining=remaining, limit=COMP_LIMIT_PER_TIMESLOT)
|
comp_footer = t(
|
||||||
|
lang,
|
||||||
|
"comp.remaining_footer_combined",
|
||||||
|
user_remaining=user_remaining,
|
||||||
|
user_limit=COMP_LIMIT_PER_USER_PER_TIMESLOT,
|
||||||
|
server_remaining=server_remaining,
|
||||||
|
server_limit=COMP_LIMIT_PER_TIMESLOT,
|
||||||
|
)
|
||||||
|
|
||||||
squadron_short = squadron_short.upper()
|
squadron_short = squadron_short.upper()
|
||||||
comp_dir = STORAGE_DIR / "COMPS"
|
comp_dir = STORAGE_DIR / "COMPS"
|
||||||
|
|||||||
+4
-1
@@ -118,7 +118,10 @@
|
|||||||
"no_players_recorded": "Žádní hráči nezaznamenáni.",
|
"no_players_recorded": "Žádní hráči nezaznamenáni.",
|
||||||
"limit_reached_title": "Limit sestav dosažen",
|
"limit_reached_title": "Limit sestav dosažen",
|
||||||
"limit_reached_desc": "Tento server vyčerpal všech {limit} vyhledávání sestav pro tento časový slot. Předplaťte si (pomocí /unlock) neomezený přístup nebo počkejte na další časový slot.",
|
"limit_reached_desc": "Tento server vyčerpal všech {limit} vyhledávání sestav pro tento časový slot. Předplaťte si (pomocí /unlock) neomezený přístup nebo počkejte na další časový slot.",
|
||||||
"remaining_footer": "{remaining}/{limit} vyhledávání sestav zbývá v tomto časovém slotu"
|
"user_limit_reached_desc": "Vyčerpal jsi všech {limit} svých osobních vyhledávání sestav pro tento časový slot. Předplaťte si (pomocí /unlock) neomezený přístup nebo počkejte na další časový slot — ostatní členové serveru mohou stále využít zbývající serverovou kvótu.",
|
||||||
|
"remaining_footer": "{remaining}/{limit} vyhledávání sestav zbývá v tomto časovém slotu",
|
||||||
|
"user_remaining_footer": "{remaining}/{limit} osobních vyhledávání sestav zbývá v tomto časovém slotu",
|
||||||
|
"remaining_footer_combined": "{user_remaining}/{user_limit} osobních · {server_remaining}/{server_limit} serverových vyhledávání sestav zbývá v tomto časovém slotu"
|
||||||
},
|
},
|
||||||
"quick_log": {
|
"quick_log": {
|
||||||
"invalid_type": "Typ lze nastavit pouze na Logy, Body, Žebříček, Týdenní BR nebo Oba.",
|
"invalid_type": "Typ lze nastavit pouze na Logy, Body, Žebříček, Týdenní BR nebo Oba.",
|
||||||
|
|||||||
+4
-1
@@ -118,7 +118,10 @@
|
|||||||
"no_players_recorded": "Keine Spieler erfasst.",
|
"no_players_recorded": "Keine Spieler erfasst.",
|
||||||
"limit_reached_title": "Aufstellungslimit erreicht",
|
"limit_reached_title": "Aufstellungslimit erreicht",
|
||||||
"limit_reached_desc": "Dieser Server hat alle {limit} Aufstellungsabfragen für diesen Zeitslot verbraucht. Abonniere (mit /unlock) für unbegrenzten Zugang oder warte auf den nächsten Zeitslot.",
|
"limit_reached_desc": "Dieser Server hat alle {limit} Aufstellungsabfragen für diesen Zeitslot verbraucht. Abonniere (mit /unlock) für unbegrenzten Zugang oder warte auf den nächsten Zeitslot.",
|
||||||
"remaining_footer": "{remaining}/{limit} Aufstellungsabfragen übrig in diesem Zeitslot"
|
"user_limit_reached_desc": "Du hast alle {limit} deiner persönlichen Aufstellungsabfragen für diesen Zeitslot verbraucht. Abonniere (mit /unlock) für unbegrenzten Zugang oder warte auf den nächsten Zeitslot — andere Mitglieder dieses Servers können das verbleibende Server-Kontingent weiterhin nutzen.",
|
||||||
|
"remaining_footer": "{remaining}/{limit} Aufstellungsabfragen übrig in diesem Zeitslot",
|
||||||
|
"user_remaining_footer": "{remaining}/{limit} persönliche Aufstellungsabfragen übrig in diesem Zeitslot",
|
||||||
|
"remaining_footer_combined": "{user_remaining}/{user_limit} persönlich · {server_remaining}/{server_limit} Server-Aufstellungsabfragen übrig in diesem Zeitslot"
|
||||||
},
|
},
|
||||||
"quick_log": {
|
"quick_log": {
|
||||||
"invalid_type": "Typ kann nur auf Logs, Punkte, Leaderboard, Wöchentlicher BR oder Beide gesetzt werden.",
|
"invalid_type": "Typ kann nur auf Logs, Punkte, Leaderboard, Wöchentlicher BR oder Beide gesetzt werden.",
|
||||||
|
|||||||
+4
-1
@@ -118,7 +118,10 @@
|
|||||||
"no_players_recorded": "No players recorded.",
|
"no_players_recorded": "No players recorded.",
|
||||||
"limit_reached_title": "Comp Limit Reached",
|
"limit_reached_title": "Comp Limit Reached",
|
||||||
"limit_reached_desc": "This server has used all {limit} comp lookups for this timeslot. Subscribe (with /unlock) for unlimited access or wait for the next timeslot.",
|
"limit_reached_desc": "This server has used all {limit} comp lookups for this timeslot. Subscribe (with /unlock) for unlimited access or wait for the next timeslot.",
|
||||||
"remaining_footer": "{remaining}/{limit} comp lookups remaining this timeslot"
|
"user_limit_reached_desc": "You have used all {limit} of your personal comp lookups for this timeslot. Subscribe (with /unlock) for unlimited access or wait for the next timeslot — other members of this server can still use the remaining server quota.",
|
||||||
|
"remaining_footer": "{remaining}/{limit} comp lookups remaining this timeslot",
|
||||||
|
"user_remaining_footer": "{remaining}/{limit} personal comp lookups remaining this timeslot",
|
||||||
|
"remaining_footer_combined": "{user_remaining}/{user_limit} personal · {server_remaining}/{server_limit} server comp lookups remaining this timeslot"
|
||||||
},
|
},
|
||||||
"quick_log": {
|
"quick_log": {
|
||||||
"invalid_type": "Type can only be set to Logs, Points, Leaderboard, Weekly BR, or Both.",
|
"invalid_type": "Type can only be set to Logs, Points, Leaderboard, Weekly BR, or Both.",
|
||||||
|
|||||||
+4
-1
@@ -118,7 +118,10 @@
|
|||||||
"no_players_recorded": "No hay jugadores registrados.",
|
"no_players_recorded": "No hay jugadores registrados.",
|
||||||
"limit_reached_title": "Límite de comps alcanzado",
|
"limit_reached_title": "Límite de comps alcanzado",
|
||||||
"limit_reached_desc": "Este servidor ha usado las {limit} consultas de comps para este horario. Suscríbete (con /unlock) para acceso ilimitado o espera al siguiente horario.",
|
"limit_reached_desc": "Este servidor ha usado las {limit} consultas de comps para este horario. Suscríbete (con /unlock) para acceso ilimitado o espera al siguiente horario.",
|
||||||
"remaining_footer": "{remaining}/{limit} consultas de comps restantes en este horario"
|
"user_limit_reached_desc": "Has usado las {limit} consultas de comps personales para este horario. Suscríbete (con /unlock) para acceso ilimitado o espera al siguiente horario — otros miembros del servidor aún pueden usar la cuota restante del servidor.",
|
||||||
|
"remaining_footer": "{remaining}/{limit} consultas de comps restantes en este horario",
|
||||||
|
"user_remaining_footer": "{remaining}/{limit} consultas de comps personales restantes en este horario",
|
||||||
|
"remaining_footer_combined": "{user_remaining}/{user_limit} personales · {server_remaining}/{server_limit} de servidor consultas de comps restantes en este horario"
|
||||||
},
|
},
|
||||||
"quick_log": {
|
"quick_log": {
|
||||||
"invalid_type": "El tipo solo puede ser Logs, Puntos, Clasificación, BR Semanal o Ambos.",
|
"invalid_type": "El tipo solo puede ser Logs, Puntos, Clasificación, BR Semanal o Ambos.",
|
||||||
|
|||||||
+4
-1
@@ -118,7 +118,10 @@
|
|||||||
"no_players_recorded": "Aucun joueur enregistré.",
|
"no_players_recorded": "Aucun joueur enregistré.",
|
||||||
"limit_reached_title": "Limite de comps atteinte",
|
"limit_reached_title": "Limite de comps atteinte",
|
||||||
"limit_reached_desc": "Ce serveur a utilisé les {limit} recherches de comps pour ce créneau. Abonnez-vous (avec /unlock) pour un accès illimité ou attendez le prochain créneau.",
|
"limit_reached_desc": "Ce serveur a utilisé les {limit} recherches de comps pour ce créneau. Abonnez-vous (avec /unlock) pour un accès illimité ou attendez le prochain créneau.",
|
||||||
"remaining_footer": "{remaining}/{limit} recherches de comps restantes pour ce créneau"
|
"user_limit_reached_desc": "Tu as utilisé tes {limit} recherches de comps personnelles pour ce créneau. Abonnez-vous (avec /unlock) pour un accès illimité ou attendez le prochain créneau — les autres membres du serveur peuvent encore utiliser le quota restant du serveur.",
|
||||||
|
"remaining_footer": "{remaining}/{limit} recherches de comps restantes pour ce créneau",
|
||||||
|
"user_remaining_footer": "{remaining}/{limit} recherches de comps personnelles restantes pour ce créneau",
|
||||||
|
"remaining_footer_combined": "{user_remaining}/{user_limit} personnelles · {server_remaining}/{server_limit} serveur recherches de comps restantes pour ce créneau"
|
||||||
},
|
},
|
||||||
"quick_log": {
|
"quick_log": {
|
||||||
"invalid_type": "Le type ne peut être défini que sur Logs, Points, Classement, BR Hebdomadaire ou Les deux.",
|
"invalid_type": "Le type ne peut être défini que sur Logs, Points, Classement, BR Hebdomadaire ou Les deux.",
|
||||||
|
|||||||
+4
-1
@@ -118,7 +118,10 @@
|
|||||||
"no_players_recorded": "Nessun giocatore registrato.",
|
"no_players_recorded": "Nessun giocatore registrato.",
|
||||||
"limit_reached_title": "Limite composizioni raggiunto",
|
"limit_reached_title": "Limite composizioni raggiunto",
|
||||||
"limit_reached_desc": "Questo server ha esaurito tutte le {limit} ricerche di composizioni per questo slot. Abbonati (con /unlock) per accesso illimitato o attendi il prossimo slot.",
|
"limit_reached_desc": "Questo server ha esaurito tutte le {limit} ricerche di composizioni per questo slot. Abbonati (con /unlock) per accesso illimitato o attendi il prossimo slot.",
|
||||||
"remaining_footer": "{remaining}/{limit} ricerche di composizioni rimanenti in questo slot"
|
"user_limit_reached_desc": "Hai esaurito tutte le {limit} ricerche di composizioni personali per questo slot. Abbonati (con /unlock) per accesso illimitato o attendi il prossimo slot — gli altri membri del server possono ancora usare la quota rimanente del server.",
|
||||||
|
"remaining_footer": "{remaining}/{limit} ricerche di composizioni rimanenti in questo slot",
|
||||||
|
"user_remaining_footer": "{remaining}/{limit} ricerche di composizioni personali rimanenti in questo slot",
|
||||||
|
"remaining_footer_combined": "{user_remaining}/{user_limit} personali · {server_remaining}/{server_limit} server ricerche di composizioni rimanenti in questo slot"
|
||||||
},
|
},
|
||||||
"quick_log": {
|
"quick_log": {
|
||||||
"invalid_type": "Il tipo può essere impostato solo su Log, Punti, Classifica, BR Settimanale o Entrambi.",
|
"invalid_type": "Il tipo può essere impostato solo su Log, Punti, Classifica, BR Settimanale o Entrambi.",
|
||||||
|
|||||||
+4
-1
@@ -118,7 +118,10 @@
|
|||||||
"no_players_recorded": "Brak zarejestrowanych graczy.",
|
"no_players_recorded": "Brak zarejestrowanych graczy.",
|
||||||
"limit_reached_title": "Limit składów osiągnięty",
|
"limit_reached_title": "Limit składów osiągnięty",
|
||||||
"limit_reached_desc": "Ten serwer wykorzystał wszystkie {limit} wyszukiwań składów w tym slocie czasowym. Subskrybuj (za pomocą /unlock) aby uzyskać nieograniczony dostęp lub poczekaj na następny slot.",
|
"limit_reached_desc": "Ten serwer wykorzystał wszystkie {limit} wyszukiwań składów w tym slocie czasowym. Subskrybuj (za pomocą /unlock) aby uzyskać nieograniczony dostęp lub poczekaj na następny slot.",
|
||||||
"remaining_footer": "{remaining}/{limit} wyszukiwań składów pozostało w tym slocie czasowym"
|
"user_limit_reached_desc": "Wykorzystałeś wszystkie {limit} swoich osobistych wyszukiwań składów w tym slocie czasowym. Subskrybuj (za pomocą /unlock) aby uzyskać nieograniczony dostęp lub poczekaj na następny slot — pozostali członkowie serwera nadal mogą korzystać z pozostałej puli serwera.",
|
||||||
|
"remaining_footer": "{remaining}/{limit} wyszukiwań składów pozostało w tym slocie czasowym",
|
||||||
|
"user_remaining_footer": "{remaining}/{limit} osobistych wyszukiwań składów pozostało w tym slocie czasowym",
|
||||||
|
"remaining_footer_combined": "{user_remaining}/{user_limit} osobistych · {server_remaining}/{server_limit} serwerowych wyszukiwań składów pozostało w tym slocie czasowym"
|
||||||
},
|
},
|
||||||
"quick_log": {
|
"quick_log": {
|
||||||
"invalid_type": "Typ można ustawić tylko na Logi, Punkty, Tabela liderów, Tygodniowy BR lub Oba.",
|
"invalid_type": "Typ można ustawić tylko na Logi, Punkty, Tabela liderów, Tygodniowy BR lub Oba.",
|
||||||
|
|||||||
+4
-1
@@ -118,7 +118,10 @@
|
|||||||
"no_players_recorded": "Nenhum jogador registrado.",
|
"no_players_recorded": "Nenhum jogador registrado.",
|
||||||
"limit_reached_title": "Limite de composições atingido",
|
"limit_reached_title": "Limite de composições atingido",
|
||||||
"limit_reached_desc": "Este servidor usou todas as {limit} consultas de composições para este horário. Assine (com /unlock) para acesso ilimitado ou aguarde o próximo horário.",
|
"limit_reached_desc": "Este servidor usou todas as {limit} consultas de composições para este horário. Assine (com /unlock) para acesso ilimitado ou aguarde o próximo horário.",
|
||||||
"remaining_footer": "{remaining}/{limit} consultas de composições restantes neste horário"
|
"user_limit_reached_desc": "Você usou todas as {limit} consultas de composições pessoais para este horário. Assine (com /unlock) para acesso ilimitado ou aguarde o próximo horário — outros membros do servidor ainda podem usar a cota restante do servidor.",
|
||||||
|
"remaining_footer": "{remaining}/{limit} consultas de composições restantes neste horário",
|
||||||
|
"user_remaining_footer": "{remaining}/{limit} consultas de composições pessoais restantes neste horário",
|
||||||
|
"remaining_footer_combined": "{user_remaining}/{user_limit} pessoais · {server_remaining}/{server_limit} servidor consultas de composições restantes neste horário"
|
||||||
},
|
},
|
||||||
"quick_log": {
|
"quick_log": {
|
||||||
"invalid_type": "O tipo só pode ser definido como Logs, Pontos, Classificação, BR Semanal ou Ambos.",
|
"invalid_type": "O tipo só pode ser definido como Logs, Pontos, Classificação, BR Semanal ou Ambos.",
|
||||||
|
|||||||
+4
-1
@@ -118,7 +118,10 @@
|
|||||||
"no_players_recorded": "Нет зафиксированных игроков.",
|
"no_players_recorded": "Нет зафиксированных игроков.",
|
||||||
"limit_reached_title": "Лимит составов достигнут",
|
"limit_reached_title": "Лимит составов достигнут",
|
||||||
"limit_reached_desc": "Этот сервер использовал все {limit} запросов составов для этого таймслота. Подпишитесь (через /unlock) для безлимитного доступа или дождитесь следующего таймслота.",
|
"limit_reached_desc": "Этот сервер использовал все {limit} запросов составов для этого таймслота. Подпишитесь (через /unlock) для безлимитного доступа или дождитесь следующего таймслота.",
|
||||||
"remaining_footer": "{remaining}/{limit} запросов составов осталось в этом таймслоте"
|
"user_limit_reached_desc": "Вы использовали все {limit} ваших личных запросов составов для этого таймслота. Подпишитесь (через /unlock) для безлимитного доступа или дождитесь следующего таймслота — другие участники сервера могут продолжать использовать оставшуюся квоту сервера.",
|
||||||
|
"remaining_footer": "{remaining}/{limit} запросов составов осталось в этом таймслоте",
|
||||||
|
"user_remaining_footer": "{remaining}/{limit} личных запросов составов осталось в этом таймслоте",
|
||||||
|
"remaining_footer_combined": "{user_remaining}/{user_limit} личных · {server_remaining}/{server_limit} серверных запросов составов осталось в этом таймслоте"
|
||||||
},
|
},
|
||||||
"quick_log": {
|
"quick_log": {
|
||||||
"invalid_type": "Тип может быть только Логи, Очки, Таблица лидеров, Еженедельный BR или Все.",
|
"invalid_type": "Тип может быть только Логи, Очки, Таблица лидеров, Еженедельный BR или Все.",
|
||||||
|
|||||||
+4
-1
@@ -118,7 +118,10 @@
|
|||||||
"no_players_recorded": "Гравців не зафіксовано.",
|
"no_players_recorded": "Гравців не зафіксовано.",
|
||||||
"limit_reached_title": "Ліміт складів досягнуто",
|
"limit_reached_title": "Ліміт складів досягнуто",
|
||||||
"limit_reached_desc": "Цей сервер використав усі {limit} запитів складів для цього таймслоту. Підпишіться (через /unlock) для безлімітного доступу або зачекайте наступного таймслоту.",
|
"limit_reached_desc": "Цей сервер використав усі {limit} запитів складів для цього таймслоту. Підпишіться (через /unlock) для безлімітного доступу або зачекайте наступного таймслоту.",
|
||||||
"remaining_footer": "{remaining}/{limit} запитів складів залишилось у цьому таймслоті"
|
"user_limit_reached_desc": "Ви використали всі {limit} ваших особистих запитів складів для цього таймслоту. Підпишіться (через /unlock) для безлімітного доступу або зачекайте наступного таймслоту — інші учасники серверу все ще можуть використовувати залишок серверної квоти.",
|
||||||
|
"remaining_footer": "{remaining}/{limit} запитів складів залишилось у цьому таймслоті",
|
||||||
|
"user_remaining_footer": "{remaining}/{limit} особистих запитів складів залишилось у цьому таймслоті",
|
||||||
|
"remaining_footer_combined": "{user_remaining}/{user_limit} особистих · {server_remaining}/{server_limit} серверних запитів складів залишилось у цьому таймслоті"
|
||||||
},
|
},
|
||||||
"quick_log": {
|
"quick_log": {
|
||||||
"invalid_type": "Тип може бути тільки Логи, Очки, Таблиця лідерів, Тижневий BR або Усі.",
|
"invalid_type": "Тип може бути тільки Логи, Очки, Таблиця лідерів, Тижневий BR або Усі.",
|
||||||
|
|||||||
@@ -118,7 +118,10 @@
|
|||||||
"no_players_recorded": "没有记录到玩家。",
|
"no_players_recorded": "没有记录到玩家。",
|
||||||
"limit_reached_title": "阵容查询次数已用完",
|
"limit_reached_title": "阵容查询次数已用完",
|
||||||
"limit_reached_desc": "此服务器已用完本时段的 {limit} 次阵容查询。订阅(使用 /unlock)可获得无限访问,或等待下一个时段。",
|
"limit_reached_desc": "此服务器已用完本时段的 {limit} 次阵容查询。订阅(使用 /unlock)可获得无限访问,或等待下一个时段。",
|
||||||
"remaining_footer": "本时段剩余 {remaining}/{limit} 次免费阵容查询"
|
"user_limit_reached_desc": "您已用完本时段的 {limit} 次个人阵容查询。订阅(使用 /unlock)可获得无限访问,或等待下一个时段——此服务器的其他成员仍可使用剩余的服务器配额。",
|
||||||
|
"remaining_footer": "本时段剩余 {remaining}/{limit} 次免费阵容查询",
|
||||||
|
"user_remaining_footer": "本时段剩余 {remaining}/{limit} 次个人阵容查询",
|
||||||
|
"remaining_footer_combined": "本时段剩余阵容查询:个人 {user_remaining}/{user_limit} · 服务器 {server_remaining}/{server_limit}"
|
||||||
},
|
},
|
||||||
"quick_log": {
|
"quick_log": {
|
||||||
"invalid_type": "类型只能设置为 Logs、Points、排行榜、周BR 或 全部。",
|
"invalid_type": "类型只能设置为 Logs、Points、排行榜、周BR 或 全部。",
|
||||||
|
|||||||
+25
-2
@@ -222,8 +222,12 @@ def higher_tier(a: Optional[str], b: Optional[str]) -> Optional[str]:
|
|||||||
if ra < 0 and rb < 0:
|
if ra < 0 and rb < 0:
|
||||||
return None
|
return None
|
||||||
return a if ra >= rb else b
|
return a if ra >= rb else b
|
||||||
# Free-tier /comp cap per timeslot.
|
# Free-tier /comp caps per timeslot.
|
||||||
COMP_LIMIT_PER_TIMESLOT: int = 15
|
# Server-wide cap counts every invocation in the guild during the window.
|
||||||
|
# Per-user cap counts each user's invocations and is enforced in addition to
|
||||||
|
# the server cap, so one user maxing out can't drain the rest of the server.
|
||||||
|
COMP_LIMIT_PER_TIMESLOT: int = 25
|
||||||
|
COMP_LIMIT_PER_USER_PER_TIMESLOT: int = 10
|
||||||
|
|
||||||
# ── SQB schedule (UTC, DST-immune) ───────────────────────────────────────────
|
# ── SQB schedule (UTC, DST-immune) ───────────────────────────────────────────
|
||||||
# Edit SQB_SLOTS_POSTED and the margin constants when Gaijin changes the
|
# Edit SQB_SLOTS_POSTED and the margin constants when Gaijin changes the
|
||||||
@@ -1080,6 +1084,25 @@ async def get_comp_usage_in_timeslot(guild_id: int, since_ts: int) -> int:
|
|||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
async def get_comp_usage_in_timeslot_by_user(
|
||||||
|
guild_id: int, user_id: int, since_ts: int
|
||||||
|
) -> int:
|
||||||
|
"""Count /comp invocations for a specific user in a guild since a timestamp."""
|
||||||
|
try:
|
||||||
|
async with aiosqlite.connect(COMMAND_DATA_DB_PATH, timeout=5.0) as db:
|
||||||
|
await db.execute("PRAGMA busy_timeout=5000;")
|
||||||
|
cur = await db.execute(
|
||||||
|
"SELECT COUNT(*) FROM command_usage "
|
||||||
|
"WHERE command_name='comp' AND guild_id=? AND user_id=? AND timestamp >= ?",
|
||||||
|
(str(guild_id), str(user_id), since_ts),
|
||||||
|
)
|
||||||
|
row = await cur.fetchone()
|
||||||
|
return row[0] if row else 0
|
||||||
|
except Exception:
|
||||||
|
logging.debug("get_comp_usage_in_timeslot_by_user query failed", exc_info=True)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
def minutes_ago(unix_timestamp: int) -> str:
|
def minutes_ago(unix_timestamp: int) -> str:
|
||||||
"""Convert a unix timestamp to a human-readable 'X minutes ago' string."""
|
"""Convert a unix timestamp to a human-readable 'X minutes ago' string."""
|
||||||
if not unix_timestamp:
|
if not unix_timestamp:
|
||||||
|
|||||||
Reference in New Issue
Block a user