Merge pull request #50 from StudyLions/rewrite
This commit is contained in:
@@ -11,6 +11,8 @@ ALSO_READ = config/emojis.conf, config/secrets.conf, config/gui.conf
|
||||
|
||||
asset_path = assets
|
||||
|
||||
support_guild =
|
||||
|
||||
[ENDPOINTS]
|
||||
guild_log =
|
||||
gem_transaction =
|
||||
@@ -19,8 +21,10 @@ gem_transaction =
|
||||
log_file = bot.log
|
||||
|
||||
general_log =
|
||||
error_log =
|
||||
critical_log =
|
||||
error_log = %(general_log)
|
||||
critical_log = %(general_log)
|
||||
warning_log = %(general_log)
|
||||
warning_prefix =
|
||||
error_prefix =
|
||||
critical_prefix =
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-09-13 08:47+0300\n"
|
||||
"POT-Creation-Date: 2023-09-24 12:21+0300\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@@ -18,41 +18,41 @@ msgstr ""
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
|
||||
|
||||
#: src/modules/pomodoro/timer.py:51
|
||||
#: src/modules/pomodoro/timer.py:52
|
||||
msgctxt "timer|stage:break|name"
|
||||
msgid "BREAK"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/timer.py:52
|
||||
#: src/modules/pomodoro/timer.py:53
|
||||
msgctxt "timer|stage:focus|name"
|
||||
msgid "FOCUS"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/timer.py:158
|
||||
#: src/modules/pomodoro/timer.py:160
|
||||
#, possible-python-brace-format
|
||||
msgctxt "timer|webhook|name"
|
||||
msgid "{bot_name} Pomodoro"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/timer.py:162
|
||||
#: src/modules/pomodoro/timer.py:164
|
||||
msgctxt "timer|webhook|audit_reason"
|
||||
msgid "Pomodoro Notifications"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/timer.py:173
|
||||
#: src/modules/pomodoro/timer.py:175
|
||||
msgctxt "timer|webhook|error:insufficient_permissions"
|
||||
msgid ""
|
||||
"I require the `MANAGE_WEBHOOKS` permission to send pomodoro notifications "
|
||||
"here!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/timer.py:232
|
||||
#: src/modules/pomodoro/timer.py:234
|
||||
#, possible-python-brace-format
|
||||
msgctxt "timer|default_base_name"
|
||||
msgid "Timer {pattern}"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/timer.py:406
|
||||
#: src/modules/pomodoro/timer.py:408
|
||||
#, possible-python-brace-format
|
||||
msgctxt "timer|kicked_message"
|
||||
msgid ""
|
||||
@@ -64,20 +64,20 @@ msgid_plural ""
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
#: src/modules/pomodoro/timer.py:499
|
||||
#: src/modules/pomodoro/timer.py:501
|
||||
#, possible-python-brace-format
|
||||
msgctxt "timer|status|stage:focus|statusline"
|
||||
msgid "{channel} is now in **FOCUS**! Good luck, **BREAK** starts {timestamp}"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/timer.py:504
|
||||
#: src/modules/pomodoro/timer.py:506
|
||||
#, possible-python-brace-format
|
||||
msgctxt "timer|status|stage:break|statusline"
|
||||
msgid ""
|
||||
"{channel} is now on **BREAK**! Take a rest, **FOCUS** starts {timestamp}"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/timer.py:536
|
||||
#: src/modules/pomodoro/timer.py:538
|
||||
#, possible-python-brace-format
|
||||
msgctxt "timer|status|warningline"
|
||||
msgid ""
|
||||
@@ -85,13 +85,13 @@ msgid ""
|
||||
"next stage."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/timer.py:555
|
||||
#: src/modules/pomodoro/timer.py:557
|
||||
#, possible-python-brace-format
|
||||
msgctxt "timer|status|stopped:auto"
|
||||
msgid "Timer stopped! Join {channel} to start the timer."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/timer.py:560
|
||||
#: src/modules/pomodoro/timer.py:562
|
||||
msgctxt "timer|status|stopped:manual"
|
||||
msgid "Timer stopped! Press `Start` to restart the timer."
|
||||
msgstr ""
|
||||
@@ -116,34 +116,34 @@ msgctxt "dash:stats|dropdown|placeholder"
|
||||
msgid "Pomodoro Timer Panel"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/cog.py:82
|
||||
#: src/modules/pomodoro/cog.py:96
|
||||
msgctxt "cmd_check:ready|failed"
|
||||
msgid ""
|
||||
"I am currently restarting! The Pomodoro timers will be unavailable until I "
|
||||
"have restarted. Thank you for your patience!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/cog.py:322
|
||||
#: src/modules/pomodoro/cog.py:336
|
||||
msgctxt "cmd:timer"
|
||||
msgid "timer"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/cog.py:323
|
||||
#: src/modules/pomodoro/cog.py:337
|
||||
msgctxt "cmd:timer|desc"
|
||||
msgid "Show your current (or selected) pomodoro timer."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/cog.py:326
|
||||
#: src/modules/pomodoro/cog.py:340
|
||||
msgctxt "cmd:timer|param:channel"
|
||||
msgid "timer_channel"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/cog.py:331
|
||||
#: src/modules/pomodoro/cog.py:345
|
||||
msgctxt "cmd:timer|param:channel|desc"
|
||||
msgid "Select a timer to display (by selecting the timer voice channel)"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/cog.py:353 src/modules/pomodoro/cog.py:423
|
||||
#: src/modules/pomodoro/cog.py:367 src/modules/pomodoro/cog.py:438
|
||||
#, possible-python-brace-format
|
||||
msgctxt "cmd:timer|error:no_timers|desc"
|
||||
msgid ""
|
||||
@@ -152,7 +152,7 @@ msgid ""
|
||||
"rent a private room with {room_cmd} and create one yourself!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/cog.py:367
|
||||
#: src/modules/pomodoro/cog.py:381
|
||||
#, possible-python-brace-format
|
||||
msgctxt "cmd:timer|error:no_channel|desc"
|
||||
msgid ""
|
||||
@@ -161,7 +161,7 @@ msgid ""
|
||||
"list the available timers in this server."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/cog.py:380
|
||||
#: src/modules/pomodoro/cog.py:394
|
||||
#, possible-python-brace-format
|
||||
msgctxt "cmd:timer|error:no_timer_in_channel"
|
||||
msgid ""
|
||||
@@ -169,17 +169,17 @@ msgid ""
|
||||
"Use {timers_cmd} to list the available timers in this server."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/cog.py:396
|
||||
#: src/modules/pomodoro/cog.py:411
|
||||
msgctxt "cmd:timers"
|
||||
msgid "timers"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/cog.py:397
|
||||
#: src/modules/pomodoro/cog.py:412
|
||||
msgctxt "cmd:timers|desc"
|
||||
msgid "List the available pomodoro timer rooms."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/cog.py:436
|
||||
#: src/modules/pomodoro/cog.py:451
|
||||
#, possible-python-brace-format
|
||||
msgctxt "cmd:timer|error:no_visible_timers|desc"
|
||||
msgid ""
|
||||
@@ -188,13 +188,13 @@ msgid ""
|
||||
"with {room_cmd} and create one yourself!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/cog.py:449
|
||||
#: src/modules/pomodoro/cog.py:464
|
||||
#, possible-python-brace-format
|
||||
msgctxt "cmd:timers|embed:timer_list|title"
|
||||
msgid "Pomodoro Timer Rooms in **{guild}**"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/cog.py:458
|
||||
#: src/modules/pomodoro/cog.py:473
|
||||
#, possible-python-brace-format
|
||||
msgctxt "cmd:timers|status:stopped_auto"
|
||||
msgid ""
|
||||
@@ -202,7 +202,7 @@ msgid ""
|
||||
"Join {channel} to restart it."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/cog.py:464
|
||||
#: src/modules/pomodoro/cog.py:479
|
||||
#, possible-python-brace-format
|
||||
msgctxt "cmd:timers|status:stopped_manual"
|
||||
msgid ""
|
||||
@@ -210,7 +210,7 @@ msgid ""
|
||||
"Join {channel} and press `Start` to start it!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/cog.py:471
|
||||
#: src/modules/pomodoro/cog.py:486
|
||||
#, possible-python-brace-format
|
||||
msgctxt "cmd:timers|status:running_focus"
|
||||
msgid ""
|
||||
@@ -218,7 +218,7 @@ msgid ""
|
||||
"Currently **focusing**, with break starting {timestamp}"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/cog.py:477
|
||||
#: src/modules/pomodoro/cog.py:492
|
||||
#, possible-python-brace-format
|
||||
msgctxt "cmd:timers|status:running_break"
|
||||
msgid ""
|
||||
@@ -226,78 +226,78 @@ msgid ""
|
||||
"Currently **resting**, with focus starting {timestamp}"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/cog.py:491
|
||||
#: src/modules/pomodoro/cog.py:506
|
||||
msgctxt "cmd:pomodoro"
|
||||
msgid "pomodoro"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/cog.py:492
|
||||
#: src/modules/pomodoro/cog.py:507
|
||||
msgctxt "cmd:pomodoro|desc"
|
||||
msgid "Create and configure pomodoro timer rooms."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/cog.py:499
|
||||
#: src/modules/pomodoro/cog.py:514
|
||||
msgctxt "cmd:pomodoro_create"
|
||||
msgid "create"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/cog.py:502
|
||||
#: src/modules/pomodoro/cog.py:517
|
||||
msgctxt "cmd:pomodoro_create|desc"
|
||||
msgid "Create a new Pomodoro timer. Requires manage channel permissions."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/cog.py:506
|
||||
#: src/modules/pomodoro/cog.py:521
|
||||
msgctxt "cmd:pomodoro_create|param:channel"
|
||||
msgid "timer_channel"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/cog.py:512
|
||||
#: src/modules/pomodoro/cog.py:527
|
||||
msgctxt "cmd:pomodoro_create|param:channel|desc"
|
||||
msgid ""
|
||||
"Voice channel to create the timer in. (Defaults to your current channel, or "
|
||||
"makes a new one.)"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/cog.py:557
|
||||
#: src/modules/pomodoro/cog.py:572
|
||||
msgctxt "cmd:pomodoro_create|new_channel|error:your_insufficient_perms|title"
|
||||
msgid "Could not create pomodoro voice channel!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/cog.py:561
|
||||
#: src/modules/pomodoro/cog.py:576
|
||||
msgctxt "cmd:pomodoro_create|new_channel|error:your_insufficient_perms"
|
||||
msgid ""
|
||||
"No `timer_channel` was provided, and you lack the 'Manage Channels` "
|
||||
"permission required to create a new timer room!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/cog.py:572
|
||||
#: src/modules/pomodoro/cog.py:587
|
||||
msgctxt "cmd:pomodoro_create|new_channel|error:my_insufficient_perms|title"
|
||||
msgid "Could not create pomodoro voice channel!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/cog.py:576
|
||||
#: src/modules/pomodoro/cog.py:591
|
||||
msgctxt "cmd:pomodoro_create|new_channel|error:my_insufficient_perms|desc"
|
||||
msgid ""
|
||||
"No `timer_channel` was provided, and I lack the 'Manage Channels' permission "
|
||||
"required to create a new voice channel."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/cog.py:587
|
||||
#: src/modules/pomodoro/cog.py:602
|
||||
msgctxt "cmd:pomodoro_create|new_channel|default_name"
|
||||
msgid "Timer"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/cog.py:591
|
||||
#: src/modules/pomodoro/cog.py:606
|
||||
msgctxt "cmd:pomodoro_create|new_channel|audit_reason"
|
||||
msgid "Creating Pomodoro Voice Channel"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/cog.py:600
|
||||
#: src/modules/pomodoro/cog.py:615
|
||||
msgctxt "cmd:pomodoro_create|new_channel|error:channel_create_failed|title"
|
||||
msgid "Could not create pomodoro voice channel!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/cog.py:604
|
||||
#: src/modules/pomodoro/cog.py:619
|
||||
msgctxt "cmd:pomodoro_create|new_channel|error:channel_create_failed|desc"
|
||||
msgid ""
|
||||
"Failed to create a new pomodoro voice channel due to an unknown Discord "
|
||||
@@ -305,13 +305,13 @@ msgid ""
|
||||
"the `timer_channel` argument of this command."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/cog.py:621
|
||||
#: src/modules/pomodoro/cog.py:636
|
||||
#, possible-python-brace-format
|
||||
msgctxt "cmd:pomodoro_create|add_timer|error:timer_exists"
|
||||
msgid "A timer already exists in {channel}! Reconfigure it with {edit_cmd}."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/cog.py:635
|
||||
#: src/modules/pomodoro/cog.py:650
|
||||
#, possible-python-brace-format
|
||||
msgctxt "cmd:pomodoro_create|add_timer|error:your_insufficient_perms"
|
||||
msgid ""
|
||||
@@ -319,43 +319,43 @@ msgid ""
|
||||
"timer there!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/cog.py:684
|
||||
#: src/modules/pomodoro/cog.py:699
|
||||
msgctxt "cmd:pomodoro_create|response:success|content"
|
||||
msgid "Timer created successfully! Use the panel below to reconfigure."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/cog.py:690
|
||||
#: src/modules/pomodoro/cog.py:705
|
||||
msgctxt "cmd:pomodoro_destroy"
|
||||
msgid "destroy"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/cog.py:693
|
||||
#: src/modules/pomodoro/cog.py:708
|
||||
msgctxt "cmd:pomodoro_destroy|desc"
|
||||
msgid "Remove a pomodoro timer from a voice channel."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/cog.py:697
|
||||
#: src/modules/pomodoro/cog.py:712
|
||||
msgctxt "cmd:pomodoro_destroy|param:channel"
|
||||
msgid "timer_channel"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/cog.py:700
|
||||
#: src/modules/pomodoro/cog.py:715
|
||||
msgctxt "cmd:pomodoro_destroy|param:channel"
|
||||
msgid "Select a timer voice channel to remove the timer from."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/cog.py:718
|
||||
#: src/modules/pomodoro/cog.py:733
|
||||
msgctxt "cmd:pomodoro_destroy|error:no_timer"
|
||||
msgid "This channel doesn't have an attached pomodoro timer!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/cog.py:731
|
||||
#: src/modules/pomodoro/cog.py:746
|
||||
msgctxt "cmd:pomodoro_destroy|error:insufficient_perms|owned"
|
||||
msgid ""
|
||||
"You need to be an administrator or own this channel to remove this timer!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/cog.py:740
|
||||
#: src/modules/pomodoro/cog.py:755
|
||||
#, possible-python-brace-format
|
||||
msgctxt "cmd:pomodoro_destroy|error:insufficient_perms|notowned"
|
||||
msgid ""
|
||||
@@ -363,38 +363,38 @@ msgid ""
|
||||
"this timer!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/cog.py:751
|
||||
#: src/modules/pomodoro/cog.py:766
|
||||
#, possible-python-brace-format
|
||||
msgctxt "cmd:pomdoro_destroy|response:success|description"
|
||||
msgid "Timer successfully removed from {channel}."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/cog.py:757
|
||||
#: src/modules/pomodoro/cog.py:772
|
||||
msgctxt "cmd:pomodoro_edit"
|
||||
msgid "edit"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/cog.py:760
|
||||
#: src/modules/pomodoro/cog.py:775
|
||||
msgctxt "cmd:pomodoro_edit|desc"
|
||||
msgid "Reconfigure a pomodoro timer."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/cog.py:764
|
||||
#: src/modules/pomodoro/cog.py:779
|
||||
msgctxt "cmd:pomodoro_edit|param:channel"
|
||||
msgid "timer_channel"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/cog.py:770
|
||||
#: src/modules/pomodoro/cog.py:785
|
||||
msgctxt "cmd:pomodoro_edit|param:channel|desc"
|
||||
msgid "Select a timer voice channel to reconfigure."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/cog.py:811
|
||||
#: src/modules/pomodoro/cog.py:826
|
||||
msgctxt "cmd:pomodoro_edit|error:no_timer"
|
||||
msgid "This channel doesn't have an attached pomodoro timer to edit!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/cog.py:824
|
||||
#: src/modules/pomodoro/cog.py:839
|
||||
msgctxt "cmd:pomodoro_edit|error:insufficient_perms|role:other"
|
||||
msgid ""
|
||||
"Insufficient permissions to modifiy this timer!\n"
|
||||
@@ -402,28 +402,28 @@ msgid ""
|
||||
"manager role."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/cog.py:845
|
||||
#: src/modules/pomodoro/cog.py:860
|
||||
msgctxt "cmd:pomodoro_edit|error:insufficient_permissions|role_needed:admin"
|
||||
msgid "You need to be a guild admin to modify this option!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/cog.py:850
|
||||
#: src/modules/pomodoro/cog.py:865
|
||||
msgctxt "cmd:pomodoro_edit|error:insufficient_permissions|role_needed:owner"
|
||||
msgid "You need to be a channel owner or guild admin to modify this option!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/cog.py:855
|
||||
#: src/modules/pomodoro/cog.py:870
|
||||
msgctxt "cmd:pomodoro_edit|error:insufficient_permissions|role_needed:manager"
|
||||
msgid ""
|
||||
"You need to be a guild admin or have the manager role to modify this option!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/cog.py:891
|
||||
#: src/modules/pomodoro/cog.py:906
|
||||
msgctxt "cmd:configure_pomodoro"
|
||||
msgid "pomodoro"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/pomodoro/cog.py:892
|
||||
#: src/modules/pomodoro/cog.py:907
|
||||
msgctxt "cmd:configure_pomodoro|desc"
|
||||
msgid "Configure Pomodoro Timer System"
|
||||
msgstr ""
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-09-13 08:47+0300\n"
|
||||
"POT-Creation-Date: 2023-09-24 12:21+0300\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@@ -84,7 +84,7 @@ msgctxt "cmd:userconfig_language|button:reset|label"
|
||||
msgid "Reset"
|
||||
msgstr ""
|
||||
|
||||
#: src/babel/cog.py:251
|
||||
#: src/babel/cog.py:252
|
||||
#, possible-python-brace-format
|
||||
msgctxt "acmpl:language|no_match"
|
||||
msgid "No supported languages matching {partial}"
|
||||
@@ -219,162 +219,172 @@ msgctxt "guildset:locale|response"
|
||||
msgid "You have set the guild language to {lang}."
|
||||
msgstr ""
|
||||
|
||||
#: src/babel/enums.py:42
|
||||
msgctxt "localenames|locale:en-US"
|
||||
msgid "American English"
|
||||
msgstr ""
|
||||
|
||||
#: src/babel/enums.py:43
|
||||
msgctxt "localenames|locale:en-GB"
|
||||
msgid "British English"
|
||||
msgctxt "localenames|locale:id"
|
||||
msgid "Indonesian"
|
||||
msgstr ""
|
||||
|
||||
#: src/babel/enums.py:44
|
||||
msgctxt "localenames|locale:bg"
|
||||
msgid "Bulgarian"
|
||||
msgstr ""
|
||||
|
||||
#: src/babel/enums.py:45
|
||||
msgctxt "localenames|locale:zh-CN"
|
||||
msgid "Chinese"
|
||||
msgstr ""
|
||||
|
||||
#: src/babel/enums.py:46
|
||||
msgctxt "localenames|locale:zh-TW"
|
||||
msgid "Taiwan Chinese"
|
||||
msgstr ""
|
||||
|
||||
#: src/babel/enums.py:47
|
||||
msgctxt "localenames|locale:hr"
|
||||
msgid "Croatian"
|
||||
msgstr ""
|
||||
|
||||
#: src/babel/enums.py:48
|
||||
msgctxt "localenames|locale:cs"
|
||||
msgid "Czech"
|
||||
msgstr ""
|
||||
|
||||
#: src/babel/enums.py:49
|
||||
msgctxt "localenames|locale:da"
|
||||
msgid "Danish"
|
||||
msgstr ""
|
||||
|
||||
#: src/babel/enums.py:50
|
||||
msgctxt "localenames|locale:nl"
|
||||
msgid "Dutch"
|
||||
msgstr ""
|
||||
|
||||
#: src/babel/enums.py:51
|
||||
msgctxt "localenames|locale:fi"
|
||||
msgid "Finnish"
|
||||
msgstr ""
|
||||
|
||||
#: src/babel/enums.py:52
|
||||
msgctxt "localenames|locale:fr"
|
||||
msgid "French"
|
||||
msgstr ""
|
||||
|
||||
#: src/babel/enums.py:53
|
||||
#: src/babel/enums.py:45
|
||||
msgctxt "localenames|locale:de"
|
||||
msgid "German"
|
||||
msgstr ""
|
||||
|
||||
#: src/babel/enums.py:54
|
||||
msgctxt "localenames|locale:el"
|
||||
msgid "Greek"
|
||||
#: src/babel/enums.py:46
|
||||
msgctxt "localenames|locale:en-GB"
|
||||
msgid "English, UK"
|
||||
msgstr ""
|
||||
|
||||
#: src/babel/enums.py:55
|
||||
msgctxt "localenames|locale:hi"
|
||||
msgid "Hindi"
|
||||
#: src/babel/enums.py:47
|
||||
msgctxt "localenames|locale:en-US"
|
||||
msgid "English, US"
|
||||
msgstr ""
|
||||
|
||||
#: src/babel/enums.py:56
|
||||
msgctxt "localenames|locale:hu"
|
||||
msgid "Hungarian"
|
||||
#: src/babel/enums.py:48
|
||||
msgctxt "localenames|locale:es-ES"
|
||||
msgid "Spanish"
|
||||
msgstr ""
|
||||
|
||||
#: src/babel/enums.py:57
|
||||
#: src/babel/enums.py:49
|
||||
msgctxt "localenames|locale:fr"
|
||||
msgid "French"
|
||||
msgstr ""
|
||||
|
||||
#: src/babel/enums.py:50
|
||||
msgctxt "localenames|locale:hr"
|
||||
msgid "Croatian"
|
||||
msgstr ""
|
||||
|
||||
#: src/babel/enums.py:51
|
||||
msgctxt "localenames|locale:it"
|
||||
msgid "Italian"
|
||||
msgstr ""
|
||||
|
||||
#: src/babel/enums.py:58
|
||||
msgctxt "localenames|locale:ja"
|
||||
msgid "Japanese"
|
||||
msgstr ""
|
||||
|
||||
#: src/babel/enums.py:59
|
||||
msgctxt "localenames|locale:ko"
|
||||
msgid "Korean"
|
||||
msgstr ""
|
||||
|
||||
#: src/babel/enums.py:60
|
||||
#: src/babel/enums.py:52
|
||||
msgctxt "localenames|locale:lt"
|
||||
msgid "Lithuanian"
|
||||
msgstr ""
|
||||
|
||||
#: src/babel/enums.py:61
|
||||
#: src/babel/enums.py:53
|
||||
msgctxt "localenames|locale:hu"
|
||||
msgid "Hungarian"
|
||||
msgstr ""
|
||||
|
||||
#: src/babel/enums.py:54
|
||||
msgctxt "localenames|locale:nl"
|
||||
msgid "Dutch"
|
||||
msgstr ""
|
||||
|
||||
#: src/babel/enums.py:55
|
||||
msgctxt "localenames|locale:no"
|
||||
msgid "Norwegian"
|
||||
msgstr ""
|
||||
|
||||
#: src/babel/enums.py:62
|
||||
#: src/babel/enums.py:56
|
||||
msgctxt "localenames|locale:pl"
|
||||
msgid "Polish"
|
||||
msgstr ""
|
||||
|
||||
#: src/babel/enums.py:63
|
||||
#: src/babel/enums.py:57
|
||||
msgctxt "localenames|locale:pt-BR"
|
||||
msgid "Brazil Portuguese"
|
||||
msgid "Portuguese, Brazilian"
|
||||
msgstr ""
|
||||
|
||||
#: src/babel/enums.py:64
|
||||
#: src/babel/enums.py:58
|
||||
msgctxt "localenames|locale:ro"
|
||||
msgid "Romanian"
|
||||
msgid "Romanian, Romania"
|
||||
msgstr ""
|
||||
|
||||
#: src/babel/enums.py:65
|
||||
msgctxt "localenames|locale:ru"
|
||||
msgid "Russian"
|
||||
#: src/babel/enums.py:59
|
||||
msgctxt "localenames|locale:fi"
|
||||
msgid "Finnish"
|
||||
msgstr ""
|
||||
|
||||
#: src/babel/enums.py:66
|
||||
msgctxt "localenames|locale:es-ES"
|
||||
msgid "Spain Spanish"
|
||||
msgstr ""
|
||||
|
||||
#: src/babel/enums.py:67
|
||||
#: src/babel/enums.py:60
|
||||
msgctxt "localenames|locale:sv-SE"
|
||||
msgid "Swedish"
|
||||
msgstr ""
|
||||
|
||||
#: src/babel/enums.py:68
|
||||
msgctxt "localenames|locale:th"
|
||||
msgid "Thai"
|
||||
msgstr ""
|
||||
|
||||
#: src/babel/enums.py:69
|
||||
msgctxt "localenames|locale:tr"
|
||||
msgid "Turkish"
|
||||
msgstr ""
|
||||
|
||||
#: src/babel/enums.py:70
|
||||
msgctxt "localenames|locale:uk"
|
||||
msgid "Ukrainian"
|
||||
msgstr ""
|
||||
|
||||
#: src/babel/enums.py:71
|
||||
#: src/babel/enums.py:61
|
||||
msgctxt "localenames|locale:vi"
|
||||
msgid "Vietnamese"
|
||||
msgstr ""
|
||||
|
||||
#: src/babel/enums.py:62
|
||||
msgctxt "localenames|locale:tr"
|
||||
msgid "Turkish"
|
||||
msgstr ""
|
||||
|
||||
#: src/babel/enums.py:63
|
||||
msgctxt "localenames|locale:cs"
|
||||
msgid "Czech"
|
||||
msgstr ""
|
||||
|
||||
#: src/babel/enums.py:64
|
||||
msgctxt "localenames|locale:el"
|
||||
msgid "Greek"
|
||||
msgstr ""
|
||||
|
||||
#: src/babel/enums.py:65
|
||||
msgctxt "localenames|locale:bg"
|
||||
msgid "Bulgarian"
|
||||
msgstr ""
|
||||
|
||||
#: src/babel/enums.py:66
|
||||
msgctxt "localenames|locale:ru"
|
||||
msgid "Russian"
|
||||
msgstr ""
|
||||
|
||||
#: src/babel/enums.py:67
|
||||
msgctxt "localenames|locale:uk"
|
||||
msgid "Ukrainian"
|
||||
msgstr ""
|
||||
|
||||
#: src/babel/enums.py:68
|
||||
msgctxt "localenames|locale:hi"
|
||||
msgid "Hindi"
|
||||
msgstr ""
|
||||
|
||||
#: src/babel/enums.py:69
|
||||
msgctxt "localenames|locale:th"
|
||||
msgid "Thai"
|
||||
msgstr ""
|
||||
|
||||
#: src/babel/enums.py:70
|
||||
msgctxt "localenames|locale:zh-CN"
|
||||
msgid "Chinese, China"
|
||||
msgstr ""
|
||||
|
||||
#: src/babel/enums.py:71
|
||||
msgctxt "localenames|locale:ja"
|
||||
msgid "Japanese"
|
||||
msgstr ""
|
||||
|
||||
#: src/babel/enums.py:72
|
||||
msgctxt "localenames|locale:zh-TW"
|
||||
msgid "Chinese, Taiwan"
|
||||
msgstr ""
|
||||
|
||||
#: src/babel/enums.py:73
|
||||
msgctxt "localenames|locale:ko"
|
||||
msgid "Korean"
|
||||
msgstr ""
|
||||
|
||||
#: src/babel/enums.py:78
|
||||
msgctxt "localenames|locale:he"
|
||||
msgid "Hebrew"
|
||||
msgstr ""
|
||||
|
||||
#: src/babel/enums.py:73
|
||||
msgctxt "localenames|locale:he_IL"
|
||||
msgid "Hebrew (Israel)"
|
||||
#: src/babel/enums.py:79
|
||||
msgctxt "localenames|locale:he-IL"
|
||||
msgid "Hebrew"
|
||||
msgstr ""
|
||||
|
||||
#: src/babel/enums.py:80
|
||||
msgctxt "localenames|locale:test"
|
||||
msgid "Test Language"
|
||||
msgstr ""
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-09-13 08:47+0300\n"
|
||||
"POT-Creation-Date: 2023-09-24 12:21+0300\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-09-13 08:47+0300\n"
|
||||
"POT-Creation-Date: 2023-09-24 12:21+0300\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-09-13 08:47+0300\n"
|
||||
"POT-Creation-Date: 2023-09-24 12:21+0300\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-09-13 08:47+0300\n"
|
||||
"POT-Creation-Date: 2023-09-24 12:21+0300\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@@ -17,74 +17,74 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=CHARSET\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#: src/modules/sysadmin/exec_cog.py:257
|
||||
#: src/modules/sysadmin/exec_cog.py:269
|
||||
msgctxt "ward:sys_admin|failed"
|
||||
msgid "You must be a bot owner to do this!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/sysadmin/exec_cog.py:262
|
||||
#: src/modules/sysadmin/exec_cog.py:274
|
||||
msgid "async"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/sysadmin/exec_cog.py:263
|
||||
#: src/modules/sysadmin/exec_cog.py:275
|
||||
msgid "Execute arbitrary code with Exec"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/sysadmin/exec_cog.py:325
|
||||
#: src/modules/sysadmin/exec_cog.py:337
|
||||
msgctxt "command"
|
||||
msgid "eval"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/sysadmin/exec_cog.py:326
|
||||
#: src/modules/sysadmin/exec_cog.py:338
|
||||
msgctxt "command:eval"
|
||||
msgid "Execute arbitrary code with Eval"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/sysadmin/exec_cog.py:329
|
||||
#: src/modules/sysadmin/exec_cog.py:341
|
||||
msgctxt "command:eval|param:string"
|
||||
msgid "Code to evaluate."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/sysadmin/exec_cog.py:336
|
||||
#: src/modules/sysadmin/exec_cog.py:348
|
||||
msgctxt "command"
|
||||
msgid "asyncall"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/sysadmin/exec_cog.py:337
|
||||
#: src/modules/sysadmin/exec_cog.py:349
|
||||
msgctxt "command:asyncall|desc"
|
||||
msgid "Execute arbitrary code on all shards."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/sysadmin/exec_cog.py:340
|
||||
#: src/modules/sysadmin/exec_cog.py:352
|
||||
msgctxt "command:asyncall|param:string"
|
||||
msgid "Cross-shard code to execute. Cannot reference ctx!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/sysadmin/exec_cog.py:341
|
||||
#: src/modules/sysadmin/exec_cog.py:353
|
||||
msgctxt "command:asyncall|param:target"
|
||||
msgid "Target shard app name, see autocomplete for options."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/sysadmin/exec_cog.py:384
|
||||
#: src/modules/sysadmin/exec_cog.py:396
|
||||
msgid "reload"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/sysadmin/exec_cog.py:385
|
||||
#: src/modules/sysadmin/exec_cog.py:397
|
||||
msgid "Reload a given LionBot extension. Launches an ExecUI."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/sysadmin/exec_cog.py:388
|
||||
#: src/modules/sysadmin/exec_cog.py:400
|
||||
msgid "Name of the extension to reload. See autocomplete for options."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/sysadmin/exec_cog.py:389
|
||||
#: src/modules/sysadmin/exec_cog.py:401
|
||||
msgid "Whether to force an extension reload even if it doesn't exist."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/sysadmin/exec_cog.py:425
|
||||
#: src/modules/sysadmin/exec_cog.py:437
|
||||
msgid "shutdown"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/sysadmin/exec_cog.py:426
|
||||
#: src/modules/sysadmin/exec_cog.py:438
|
||||
msgid "Shutdown (or restart) the client."
|
||||
msgstr ""
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-09-13 08:47+0300\n"
|
||||
"POT-Creation-Date: 2023-09-24 12:21+0300\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-09-13 08:47+0300\n"
|
||||
"POT-Creation-Date: 2023-09-24 12:21+0300\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-09-13 08:47+0300\n"
|
||||
"POT-Creation-Date: 2023-09-24 12:21+0300\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@@ -27,45 +27,47 @@ msgctxt "settype:coin|parse|error:notinteger"
|
||||
msgid "The coin quantity must be a positive integer!"
|
||||
msgstr ""
|
||||
|
||||
#: src/core/setting_types.py:54
|
||||
#: src/core/setting_types.py:55
|
||||
#, possible-python-brace-format
|
||||
msgctxt "settype:coin|parse|error:too_large"
|
||||
msgid "Provided number of coins was too high!"
|
||||
msgid "You cannot set this to more than {coin}**{max}**!"
|
||||
msgstr ""
|
||||
|
||||
#: src/core/setting_types.py:60
|
||||
msgctxt "settype:coin|parse|error:too_large"
|
||||
msgid "Provided number of coins was too low!"
|
||||
#: src/core/setting_types.py:63
|
||||
#, possible-python-brace-format
|
||||
msgctxt "settype:coin|parse|error:too_small"
|
||||
msgid "You cannot set this to less than {coin}**{min}**!"
|
||||
msgstr ""
|
||||
|
||||
#: src/core/setting_types.py:71
|
||||
#: src/core/setting_types.py:75
|
||||
#, possible-python-brace-format
|
||||
msgctxt "settype:coin|formatted"
|
||||
msgid "{coin}**{amount}**"
|
||||
msgstr ""
|
||||
|
||||
#: src/core/setting_types.py:87
|
||||
#: src/core/setting_types.py:91
|
||||
msgctxt "settype:message|accepts"
|
||||
msgid "JSON formatted raw message data"
|
||||
msgstr ""
|
||||
|
||||
#: src/core/setting_types.py:102
|
||||
#: src/core/setting_types.py:106
|
||||
msgctxt "settype:message|download|error:not_json"
|
||||
msgid "The attached message data is not a JSON file!"
|
||||
msgstr ""
|
||||
|
||||
#: src/core/setting_types.py:107
|
||||
#: src/core/setting_types.py:111
|
||||
msgctxt "settype:message|download|error:size"
|
||||
msgid "The attached message data is too large!"
|
||||
msgstr ""
|
||||
|
||||
#: src/core/setting_types.py:116
|
||||
#: src/core/setting_types.py:120
|
||||
msgctxt "settype:message|download|error:decoding"
|
||||
msgid ""
|
||||
"Could not decode the message data. Please ensure it is saved with the "
|
||||
"`UTF-8` encoding."
|
||||
msgstr ""
|
||||
|
||||
#: src/core/setting_types.py:173
|
||||
#: src/core/setting_types.py:177
|
||||
#, possible-python-brace-format
|
||||
msgctxt "settype:message|error_suffix"
|
||||
msgid ""
|
||||
@@ -73,7 +75,7 @@ msgid ""
|
||||
"({link})."
|
||||
msgstr ""
|
||||
|
||||
#: src/core/setting_types.py:185
|
||||
#: src/core/setting_types.py:189
|
||||
#, possible-python-brace-format
|
||||
msgctxt "settype:message|error:invalid_json"
|
||||
msgid ""
|
||||
@@ -81,34 +83,34 @@ msgid ""
|
||||
"`{error}`"
|
||||
msgstr ""
|
||||
|
||||
#: src/core/setting_types.py:193
|
||||
#: src/core/setting_types.py:197
|
||||
msgctxt "settype:message|error:json_missing_keys"
|
||||
msgid ""
|
||||
"Message data must be a JSON object with at least one of the following "
|
||||
"fields: `content`, `embed`, `embeds`"
|
||||
msgstr ""
|
||||
|
||||
#: src/core/setting_types.py:202
|
||||
#: src/core/setting_types.py:206
|
||||
msgctxt "settype:message|error:json_embed_type"
|
||||
msgid "`embed` field must be a valid JSON object."
|
||||
msgstr ""
|
||||
|
||||
#: src/core/setting_types.py:210
|
||||
#: src/core/setting_types.py:214
|
||||
msgctxt "settype:message|error:json_embeds_type"
|
||||
msgid "`embeds` field must be a list."
|
||||
msgstr ""
|
||||
|
||||
#: src/core/setting_types.py:217
|
||||
#: src/core/setting_types.py:221
|
||||
msgctxt "settype:message|error:json_embed_embeds"
|
||||
msgid "Message data cannot include both `embed` and `embeds`."
|
||||
msgstr ""
|
||||
|
||||
#: src/core/setting_types.py:225
|
||||
#: src/core/setting_types.py:229
|
||||
msgctxt "settype:message|error:json_content_type"
|
||||
msgid "`content` field must be a string."
|
||||
msgstr ""
|
||||
|
||||
#: src/core/setting_types.py:241
|
||||
#: src/core/setting_types.py:245
|
||||
#, possible-python-brace-format
|
||||
msgctxt "ui:settype:message|error:embed_conversion"
|
||||
msgid ""
|
||||
@@ -116,7 +118,7 @@ msgid ""
|
||||
"**Error:** `{exception}`"
|
||||
msgstr ""
|
||||
|
||||
#: src/core/setting_types.py:269
|
||||
#: src/core/setting_types.py:273
|
||||
msgctxt "settype:message|format:too_long"
|
||||
msgid "Too long to display! See Preview."
|
||||
msgstr ""
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-09-13 08:47+0300\n"
|
||||
"POT-Creation-Date: 2023-09-24 12:21+0300\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@@ -44,7 +44,7 @@ msgstr ""
|
||||
|
||||
#: src/modules/member_admin/settingui.py:213
|
||||
msgctxt "ui:memberadmin|embed|title"
|
||||
msgid "Member Admin Configuration Panel"
|
||||
msgid "Greetings and Initial Roles Panel"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/member_admin/settingui.py:257
|
||||
@@ -67,39 +67,39 @@ msgctxt "dash:member_admin|section:initial_roles|name"
|
||||
msgid "Initial Roles ({commands[configure welcome]})"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/member_admin/cog.py:234
|
||||
#: src/modules/member_admin/cog.py:239
|
||||
msgctxt "cmd:resetmember"
|
||||
msgid "resetmember"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/member_admin/cog.py:237
|
||||
#: src/modules/member_admin/cog.py:242
|
||||
msgctxt "cmd:resetmember|desc"
|
||||
msgid "Reset (server-associated) member data for the target member or user."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/member_admin/cog.py:241
|
||||
#: src/modules/member_admin/cog.py:246
|
||||
msgctxt "cmd:resetmember|param:target"
|
||||
msgid "member_to_reset"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/member_admin/cog.py:242
|
||||
#: src/modules/member_admin/cog.py:247
|
||||
msgctxt "cmd:resetmember|param:saved_roles"
|
||||
msgid "saved_roles"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/member_admin/cog.py:247
|
||||
#: src/modules/member_admin/cog.py:252
|
||||
msgctxt "cmd:resetmember|param:target|desc"
|
||||
msgid "Choose the member (current or past) you want to reset."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/member_admin/cog.py:251
|
||||
#: src/modules/member_admin/cog.py:256
|
||||
msgctxt "cmd:resetmember|param:saved_roles|desc"
|
||||
msgid ""
|
||||
"Clear the saved roles for this member, so their past roles are not restored "
|
||||
"on rejoin."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/member_admin/cog.py:278
|
||||
#: src/modules/member_admin/cog.py:283
|
||||
#, possible-python-brace-format
|
||||
msgctxt "cmd:resetmember|reset:saved_roles|success"
|
||||
msgid ""
|
||||
@@ -107,17 +107,17 @@ msgid ""
|
||||
"roles if they rejoin."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/member_admin/cog.py:286
|
||||
#: src/modules/member_admin/cog.py:291
|
||||
msgctxt "cmd:resetmember|error:nothing_to_do"
|
||||
msgid "No reset operation selected, nothing to do."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/member_admin/cog.py:302
|
||||
#: src/modules/member_admin/cog.py:307
|
||||
msgctxt "cmd:configure_welcome"
|
||||
msgid "welcome"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/member_admin/cog.py:305
|
||||
#: src/modules/member_admin/cog.py:310
|
||||
msgctxt "cmd:configure_welcome|desc"
|
||||
msgid "Configure new member greetings and roles."
|
||||
msgstr ""
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-09-13 08:47+0300\n"
|
||||
"POT-Creation-Date: 2023-09-24 12:21+0300\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-09-13 08:47+0300\n"
|
||||
"POT-Creation-Date: 2023-09-24 12:21+0300\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-09-13 08:47+0300\n"
|
||||
"POT-Creation-Date: 2023-09-24 12:21+0300\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-09-13 08:47+0300\n"
|
||||
"POT-Creation-Date: 2023-09-24 12:21+0300\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-09-13 08:47+0300\n"
|
||||
"POT-Creation-Date: 2023-09-24 12:21+0300\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@@ -32,17 +32,22 @@ msgctxt "cmd:configure_ranks|param:rank_type|choice:message"
|
||||
msgid "Message"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/cog.py:406
|
||||
#: src/modules/ranks/cog.py:495
|
||||
msgctxt "event:rank_update|embed:notify"
|
||||
msgid "New Activity Rank Attained!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/cog.py:516
|
||||
#: src/modules/ranks/cog.py:601
|
||||
msgctxt "rank_refresh|error:cannot_chunk|desc"
|
||||
msgid "Could not retrieve member list from Discord. Please try again later."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/cog.py:614
|
||||
msgctxt "rank_refresh|error:roles_dne|desc"
|
||||
msgid "Some ranks have invalid or deleted roles! Please remove them first."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/cog.py:526
|
||||
#: src/modules/ranks/cog.py:624
|
||||
#, possible-python-brace-format
|
||||
msgctxt "rank_refresh|error:unassignable_roles|desc"
|
||||
msgid ""
|
||||
@@ -50,36 +55,36 @@ msgid ""
|
||||
"{roles}"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/cog.py:596
|
||||
#: src/modules/ranks/cog.py:694
|
||||
msgctxt "rank_refresh|remove_roles|audit"
|
||||
msgid "Removing invalid rank role."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/cog.py:610
|
||||
#: src/modules/ranks/cog.py:708
|
||||
#, possible-python-brace-format
|
||||
msgctxt "rank_refresh|remove_roles|small_error"
|
||||
msgid "*Could not remove ranks from {member}*"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/cog.py:617
|
||||
#: src/modules/ranks/cog.py:715
|
||||
msgctxt "rank_refresh|remove_roles|error:too_many_issues"
|
||||
msgid ""
|
||||
"Too many issues occurred while removing ranks! Please check my permissions "
|
||||
"and try again in a few minutes."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/cog.py:631
|
||||
#: src/modules/ranks/cog.py:729
|
||||
msgctxt "rank_refresh|add_roles|audit"
|
||||
msgid "Adding rank role from refresh"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/cog.py:645
|
||||
#: src/modules/ranks/cog.py:743
|
||||
#, possible-python-brace-format
|
||||
msgctxt "rank_refresh|add_roles|small_error"
|
||||
msgid "*Could not add {role} to {member}*"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/cog.py:652
|
||||
#: src/modules/ranks/cog.py:750
|
||||
msgctxt "rank_refresh|add_roles|error:too_many_issues"
|
||||
msgid ""
|
||||
"Too many issues occurred while adding ranks! Please check my permissions and "
|
||||
@@ -87,22 +92,22 @@ msgid ""
|
||||
msgstr ""
|
||||
|
||||
#. ---------- Commands ----------
|
||||
#: src/modules/ranks/cog.py:677
|
||||
#: src/modules/ranks/cog.py:775
|
||||
msgctxt "cmd:ranks"
|
||||
msgid "ranks"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/cog.py:709
|
||||
#: src/modules/ranks/cog.py:807
|
||||
msgctxt "cmd:configure_ranks"
|
||||
msgid "ranks"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/cog.py:710
|
||||
#: src/modules/ranks/cog.py:808
|
||||
msgctxt "cmd:configure_ranks|desc"
|
||||
msgid "Configure Activity Ranks"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/cog.py:770
|
||||
#: src/modules/ranks/cog.py:868
|
||||
#, possible-python-brace-format
|
||||
msgctxt ""
|
||||
"cmd:configure_ranks|response:updated|setting:notification|withdm_withchannel"
|
||||
@@ -111,20 +116,20 @@ msgid ""
|
||||
"otherwise to {channel}"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/cog.py:776
|
||||
#: src/modules/ranks/cog.py:874
|
||||
msgctxt ""
|
||||
"cmd:configure_ranks|response:updated|setting:notification|withdm_nochannel"
|
||||
msgid "Rank update notifications will be sent via **direct message**."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/cog.py:782
|
||||
#: src/modules/ranks/cog.py:880
|
||||
#, possible-python-brace-format
|
||||
msgctxt ""
|
||||
"cmd:configure_ranks|response:updated|setting:notification|nodm_withchannel"
|
||||
msgid "Rank update notifications will be sent to {channel}."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/cog.py:787
|
||||
#: src/modules/ranks/cog.py:885
|
||||
msgctxt ""
|
||||
"cmd:configure_ranks|response:updated|setting:notification|nodm_nochannel"
|
||||
msgid "Members will not be notified when their activity rank updates."
|
||||
@@ -346,31 +351,31 @@ msgctxt "guildset:dm_ranks|response:false"
|
||||
msgid "I will never direct message members upon rank advancement."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/preview.py:74
|
||||
#: src/modules/ranks/ui/preview.py:75
|
||||
msgctxt "ui:rank_preview|button:edit|error:role_deleted"
|
||||
msgid ""
|
||||
"The role underlying this rank no longer exists! Please select a new role "
|
||||
"from the role menu."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/preview.py:81
|
||||
#: src/modules/ranks/ui/preview.py:82
|
||||
msgctxt "ui:rank_preview|button:edit|error:role_not_assignable"
|
||||
msgid ""
|
||||
"I do not have permission to edit the underlying role! Please select a new "
|
||||
"role from the role menu, or ensure my top role is above the selected role."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/preview.py:90
|
||||
#: src/modules/ranks/ui/preview.py:91
|
||||
msgctxt "ui:rank_preview|button:edit|error|title"
|
||||
msgid "Failed to edit rank!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/preview.py:108
|
||||
#: src/modules/ranks/ui/preview.py:109
|
||||
msgctxt "ui:rank_preview|button:edit|label"
|
||||
msgid "Edit"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/preview.py:139
|
||||
#: src/modules/ranks/ui/preview.py:142
|
||||
#, possible-python-brace-format
|
||||
msgctxt "ui:rank_preview|button:delete|response:success|description|with_role"
|
||||
msgid ""
|
||||
@@ -378,24 +383,24 @@ msgid ""
|
||||
"the role."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/preview.py:144
|
||||
#: src/modules/ranks/ui/preview.py:147
|
||||
#, possible-python-brace-format
|
||||
msgctxt "ui:rank_preview|button:delete|response:success|description|no_role"
|
||||
msgid "You have deleted the rank {mention}."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/preview.py:150
|
||||
#: src/modules/ranks/ui/preview.py:153
|
||||
msgctxt "ui:rank_preview|button:delete|response:success|title"
|
||||
msgid "Rank Deleted"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/preview.py:160
|
||||
#: src/modules/ranks/ui/preview.py:163
|
||||
msgctxt ""
|
||||
"ui:rank_preview|button:delete|response:success|button:delete_role|label"
|
||||
msgid "Delete Role"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/preview.py:176
|
||||
#: src/modules/ranks/ui/preview.py:179
|
||||
#, possible-python-brace-format
|
||||
msgctxt ""
|
||||
"ui:rank_preview|button:delete|response:success|button:delete_role|response:"
|
||||
@@ -405,7 +410,7 @@ msgid ""
|
||||
"unknown error."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/preview.py:182
|
||||
#: src/modules/ranks/ui/preview.py:185
|
||||
#, possible-python-brace-format
|
||||
msgctxt ""
|
||||
"ui:rank_preview|button:delete|response:success|button:delete_role|response:"
|
||||
@@ -413,37 +418,24 @@ msgctxt ""
|
||||
msgid "You have deleted the rank **{name}** along with the underlying role."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/preview.py:199
|
||||
#: src/modules/ranks/ui/preview.py:202
|
||||
msgctxt "ui:rank_preview|button:delete|label"
|
||||
msgid "Delete Rank"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/preview.py:219
|
||||
#, possible-python-brace-format
|
||||
msgctxt "ui:rank_preview|menu:roles|error:above_caller"
|
||||
msgid ""
|
||||
"You have insufficient permissions to assign {mention} as a rank role! You "
|
||||
"may only manage roles below your top role."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/preview.py:225
|
||||
msgctxt "ui:rank_preview|menu:roles|error:above_caller|title"
|
||||
msgid "Insufficient permissions!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/preview.py:241
|
||||
#: src/modules/ranks/ui/preview.py:232
|
||||
msgctxt "ui:rank_preview|menu:roles|error:not_assignable|suberror:is_default"
|
||||
msgid "The @everyone role cannot be removed, and cannot be a rank!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/preview.py:246
|
||||
#: src/modules/ranks/ui/preview.py:237
|
||||
msgctxt "ui:rank_preview|menu:roles|error:not_assignable|suberror:is_managed"
|
||||
msgid ""
|
||||
"The role is managed by another application or integration, and cannot be a "
|
||||
"rank!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/preview.py:251
|
||||
#: src/modules/ranks/ui/preview.py:242
|
||||
msgctxt ""
|
||||
"ui:rank_preview|menu:roles|error:not_assignable|suberror:no_permissions"
|
||||
msgid ""
|
||||
@@ -451,121 +443,121 @@ msgid ""
|
||||
"manage ranks!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/preview.py:256
|
||||
#: src/modules/ranks/ui/preview.py:247
|
||||
msgctxt "ui:rank_preview|menu:roles|error:not_assignable|suberror:above_me"
|
||||
msgid ""
|
||||
"This role is above my top role in the role hierarchy, so I cannot add or "
|
||||
"remove it!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/preview.py:262
|
||||
#: src/modules/ranks/ui/preview.py:253
|
||||
msgctxt "ui:rank_preview|menu:roles|error:not_assignable|suberror:other"
|
||||
msgid "I am not able to manage the selected role, so it cannot be a rank!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/preview.py:268
|
||||
#: src/modules/ranks/ui/preview.py:259
|
||||
msgctxt "ui:rank_preview|menu:roles|error:not_assignable|title"
|
||||
msgid "Could not update rank!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/preview.py:278
|
||||
#: src/modules/ranks/ui/preview.py:269
|
||||
msgctxt "ui:rank_preview|menu:roles|placeholder"
|
||||
msgid "Update Rank Role"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/preview.py:290
|
||||
#: src/modules/ranks/ui/preview.py:281
|
||||
msgctxt "ui:rank_preview|embed|title"
|
||||
msgid "Rank Information"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/preview.py:297
|
||||
#: src/modules/ranks/ui/preview.py:288
|
||||
msgctxt "ui:rank_preview|embed|field:role|name"
|
||||
msgid "Role"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/preview.py:304
|
||||
#: src/modules/ranks/ui/preview.py:295
|
||||
msgctxt "ui:rank_preview|embed|field:required|name"
|
||||
msgid "Required"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/preview.py:311
|
||||
#: src/modules/ranks/ui/preview.py:302
|
||||
msgctxt "ui:rank_preview|embed|field:reward|name"
|
||||
msgid "Reward"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/preview.py:320
|
||||
#: src/modules/ranks/ui/preview.py:311
|
||||
msgctxt "ui:rank_preview|embed|field:message"
|
||||
msgid "Congratulatory Message"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/refresh.py:125
|
||||
#: src/modules/ranks/ui/refresh.py:134
|
||||
msgctxt "ui:refresh_ranks|embed|title:errored"
|
||||
msgid "Could not refresh the server ranks!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/refresh.py:133
|
||||
#: src/modules/ranks/ui/refresh.py:142
|
||||
msgctxt "ui:refresh_ranks|embed|title:done"
|
||||
msgid "Rank refresh complete!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/refresh.py:139
|
||||
#: src/modules/ranks/ui/refresh.py:148
|
||||
msgctxt "ui:refresh_ranks|embed|title:working"
|
||||
msgid "Refreshing your server ranks, please wait."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/refresh.py:157
|
||||
#: src/modules/ranks/ui/refresh.py:166
|
||||
#, possible-python-brace-format
|
||||
msgctxt "ui:refresh_ranks|embed|line:ranks"
|
||||
msgid "**Loading server ranks:** {emoji}"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/refresh.py:167
|
||||
#: src/modules/ranks/ui/refresh.py:176
|
||||
#, possible-python-brace-format
|
||||
msgctxt "ui:refresh_ranks|embed|line:members"
|
||||
msgid "**Loading server members:** {emoji}"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/refresh.py:177
|
||||
#: src/modules/ranks/ui/refresh.py:186
|
||||
#, possible-python-brace-format
|
||||
msgctxt "ui:refresh_ranks|embed|line:roles"
|
||||
msgid "**Loading rank roles:** {emoji}"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/refresh.py:187
|
||||
#: src/modules/ranks/ui/refresh.py:196
|
||||
#, possible-python-brace-format
|
||||
msgctxt "ui:refresh_ranks|embed|line:compute"
|
||||
msgid "**Computing correct ranks:** {emoji}"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/refresh.py:198
|
||||
#: src/modules/ranks/ui/refresh.py:207
|
||||
msgctxt "ui:refresh_ranks|embed|field:remove|name"
|
||||
msgid "Removing invalid rank roles from members"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/refresh.py:202
|
||||
#: src/modules/ranks/ui/refresh.py:211
|
||||
#, possible-python-brace-format
|
||||
msgctxt "ui:refresh_ranks|embed|field:remove|value"
|
||||
msgid "{progress} {done}/{total} removed"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/refresh.py:213
|
||||
#: src/modules/ranks/ui/refresh.py:222
|
||||
#, possible-python-brace-format
|
||||
msgctxt "ui:refresh_ranks|embed|line:remove"
|
||||
msgid "**Removed invalid ranks:** {done}/{target}"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/refresh.py:221
|
||||
#: src/modules/ranks/ui/refresh.py:230
|
||||
msgctxt "ui:refresh_ranks|embed|field:add|name"
|
||||
msgid "Giving members their rank roles"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/refresh.py:225
|
||||
#: src/modules/ranks/ui/refresh.py:234
|
||||
#, possible-python-brace-format
|
||||
msgctxt "ui:refresh_ranks|embed|field:add|value"
|
||||
msgid "{progress} {done}/{total} given"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/refresh.py:236
|
||||
#: src/modules/ranks/ui/refresh.py:245
|
||||
#, possible-python-brace-format
|
||||
msgctxt "ui:refresh_ranks|embed|line:add"
|
||||
msgid "**Updated member ranks:** {done}/{target}"
|
||||
@@ -621,67 +613,54 @@ msgctxt "dash:rank|dropdown|placeholder"
|
||||
msgid "Activity Rank Panel"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/overview.py:94
|
||||
#: src/modules/ranks/ui/overview.py:95
|
||||
msgctxt "ui:rank_overview|button:auto|label"
|
||||
msgid "Auto Create"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/overview.py:109
|
||||
#: src/modules/ranks/ui/overview.py:110
|
||||
msgctxt "ui:rank_overview|button:refresh|label"
|
||||
msgid "Refresh Member Ranks"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/overview.py:121
|
||||
#: src/modules/ranks/ui/overview.py:122
|
||||
msgctxt "ui:rank_overview|button:clear|confirm"
|
||||
msgid "Are you sure you want to **delete all activity ranks** in this server?"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/overview.py:126
|
||||
#: src/modules/ranks/ui/overview.py:127
|
||||
msgctxt "ui:rank_overview|button:clear|confirm|button:yes"
|
||||
msgid "Yes, clear ranks"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/overview.py:132
|
||||
#: src/modules/ranks/ui/overview.py:133
|
||||
msgctxt "ui:rank_overview|button:clear|confirm|button:no"
|
||||
msgid "Cancel"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/overview.py:148
|
||||
#: src/modules/ranks/ui/overview.py:149
|
||||
msgctxt "ui:rank_overview|button:clear|label"
|
||||
msgid "Clear Ranks"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/overview.py:178
|
||||
#: src/modules/ranks/ui/overview.py:179
|
||||
msgctxt "ui:rank_overview|button:create|label"
|
||||
msgid "Create Rank"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/overview.py:194
|
||||
#, possible-python-brace-format
|
||||
msgctxt "ui:rank_overview|menu:roles|error:above_caller"
|
||||
msgid ""
|
||||
"You have insufficient permissions to assign {mention} as a rank role! You "
|
||||
"may only manage roles below your top role."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/overview.py:200
|
||||
msgctxt "ui:rank_overview|menu:roles|error:above_caller|title"
|
||||
msgid "Insufficient permissions!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/overview.py:233
|
||||
#: src/modules/ranks/ui/overview.py:222
|
||||
msgctxt "ui:rank_overview|menu:roles|error:not_assignable|suberror:is_default"
|
||||
msgid "The @everyone role cannot be removed, and cannot be a rank!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/overview.py:238
|
||||
#: src/modules/ranks/ui/overview.py:227
|
||||
msgctxt "ui:rank_overview|menu:roles|error:not_assignable|suberror:is_managed"
|
||||
msgid ""
|
||||
"The role is managed by another application or integration, and cannot be a "
|
||||
"rank!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/overview.py:243
|
||||
#: src/modules/ranks/ui/overview.py:232
|
||||
msgctxt ""
|
||||
"ui:rank_overview|menu:roles|error:not_assignable|suberror:no_permissions"
|
||||
msgid ""
|
||||
@@ -689,45 +668,71 @@ msgid ""
|
||||
"manage ranks!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/overview.py:248
|
||||
#: src/modules/ranks/ui/overview.py:237
|
||||
msgctxt "ui:rank_overview|menu:roles|error:not_assignable|suberror:above_me"
|
||||
msgid ""
|
||||
"This role is above my top role in the role hierarchy, so I cannot add or "
|
||||
"remove it!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/overview.py:254
|
||||
#: src/modules/ranks/ui/overview.py:243
|
||||
msgctxt "ui:rank_overview|menu:roles|error:not_assignable|suberror:other"
|
||||
msgid "I am not able to manage the selected role, so it cannot be a rank!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/overview.py:260
|
||||
#: src/modules/ranks/ui/overview.py:249
|
||||
msgctxt "ui:rank_overview|menu:roles|error:not_assignable|title"
|
||||
msgid "Could not create rank!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/overview.py:284
|
||||
#: src/modules/ranks/ui/overview.py:273
|
||||
msgctxt "ui:rank_overview|menu:roles|placeholder"
|
||||
msgid "Create from role"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/overview.py:301
|
||||
#: src/modules/ranks/ui/overview.py:290
|
||||
msgctxt "ui:rank_overview|menu:ranks|placeholder"
|
||||
msgid "View or edit rank"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/overview.py:387
|
||||
#: src/modules/ranks/ui/overview.py:376
|
||||
msgctxt "ui:rank_overview|embed:noranks|desc"
|
||||
msgid ""
|
||||
"No activity ranks have been set up!\n"
|
||||
"Press 'AUTO' to automatically create a standard heirachy of voice | text | "
|
||||
"xp ranks, or select a role or press Create below!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/overview.py:384
|
||||
#, possible-python-brace-format
|
||||
msgctxt "ui:rank_overview|embed|title|type:voice"
|
||||
msgid "Voice Ranks in {guild_name}"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/overview.py:389
|
||||
#, possible-python-brace-format
|
||||
msgctxt "ui:rank_overview|embed|title|type:xp"
|
||||
msgid "XP ranks in {guild_name}"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/overview.py:394
|
||||
#, possible-python-brace-format
|
||||
msgctxt "ui:rank_overview|embed|title|type:message"
|
||||
msgid "Message ranks in {guild_name}"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/overview.py:406
|
||||
msgctxt "ui:rank_overview|embed|field:note|name"
|
||||
msgid "Note"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/overview.py:393
|
||||
#: src/modules/ranks/ui/overview.py:412
|
||||
#, possible-python-brace-format
|
||||
msgctxt "ui:rank_overview|embed|field:note|value:with_season"
|
||||
msgid "Ranks are determined by activity since {timestamp}."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/overview.py:400
|
||||
#: src/modules/ranks/ui/overview.py:419
|
||||
#, possible-python-brace-format
|
||||
msgctxt "ui:rank_overview|embed|field:note|value:without_season"
|
||||
msgid ""
|
||||
@@ -736,7 +741,7 @@ msgid ""
|
||||
"ranks) set the `season_start` with {stats_cmd}"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/overview.py:407
|
||||
#: src/modules/ranks/ui/overview.py:426
|
||||
msgctxt "ui:rank_overview|embed|field:note|value|voice_addendum"
|
||||
msgid ""
|
||||
"Also note that ranks will only be updated when a member leaves a tracked "
|
||||
@@ -744,32 +749,6 @@ msgid ""
|
||||
"members manually."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/overview.py:415
|
||||
msgctxt "ui:rank_overview|embed:noranks|desc"
|
||||
msgid ""
|
||||
"No activity ranks have been set up!\n"
|
||||
"Press 'AUTO' to automatically create a standard heirachy of voice | text | "
|
||||
"xp ranks, or select a role or press Create below!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/overview.py:423
|
||||
#, possible-python-brace-format
|
||||
msgctxt "ui:rank_overview|embed|title|type:voice"
|
||||
msgid "Voice Ranks in {guild_name}"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/overview.py:428
|
||||
#, possible-python-brace-format
|
||||
msgctxt "ui:rank_overview|embed|title|type:xp"
|
||||
msgid "XP ranks in {guild_name}"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/overview.py:433
|
||||
#, possible-python-brace-format
|
||||
msgctxt "ui:rank_overview|embed|title|type:message"
|
||||
msgid "Message ranks in {guild_name}"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/ranks/ui/editor.py:33
|
||||
msgctxt "ui:rank_editor|input:role_name|label"
|
||||
msgid "Role Name"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-09-13 08:47+0300\n"
|
||||
"POT-Creation-Date: 2023-09-24 12:21+0300\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-09-13 08:47+0300\n"
|
||||
"POT-Creation-Date: 2023-09-24 12:21+0300\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@@ -349,13 +349,18 @@ msgctxt "cmd:rolemenu_edit|parse:template|success:custom"
|
||||
msgid "Now using a custom menu message."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/cog.py:994
|
||||
#: src/modules/rolemenus/cog.py:986
|
||||
msgctxt "cmd:rolemenu_edit|parse:custom_message|success"
|
||||
msgid "Custom menu message updated."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/cog.py:1001
|
||||
#, possible-python-brace-format
|
||||
msgctxt "cmd:rolemenu_edit|repost|success"
|
||||
msgid "The role menu is now available at {message}"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/cog.py:1005
|
||||
#: src/modules/rolemenus/cog.py:1012
|
||||
#, possible-python-brace-format
|
||||
msgctxt "cmd:rolemenu_edit|repost|error:forbidden"
|
||||
msgid ""
|
||||
@@ -363,7 +368,7 @@ msgid ""
|
||||
"permission in {channel}."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/cog.py:1010
|
||||
#: src/modules/rolemenus/cog.py:1017
|
||||
#, possible-python-brace-format
|
||||
msgctxt "cmd:rolemenu_edit|repost|error:unknown"
|
||||
msgid ""
|
||||
@@ -371,40 +376,40 @@ msgid ""
|
||||
"**Error:** `{exception}`"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/cog.py:1044
|
||||
#: src/modules/rolemenus/cog.py:1051
|
||||
msgctxt "cmd:rolemenu_delete"
|
||||
msgid "delmenu"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/cog.py:1047
|
||||
#: src/modules/rolemenus/cog.py:1054
|
||||
msgctxt "cmd:rolemenu_delete|desc"
|
||||
msgid "Delete a role menu."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/cog.py:1051
|
||||
#: src/modules/rolemenus/cog.py:1058
|
||||
msgctxt "cmd:rolemenu_delete|param:name"
|
||||
msgid "menu"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/cog.py:1056
|
||||
#: src/modules/rolemenus/cog.py:1063
|
||||
msgctxt "cmd:rolemenu_delete|param:name|desc"
|
||||
msgid "Name of the rolemenu to delete."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/cog.py:1071
|
||||
#: src/modules/rolemenus/cog.py:1078
|
||||
msgctxt "cmd:rolemenu_delete|error:author_perms"
|
||||
msgid ""
|
||||
"You need the `MANAGE_ROLES` permission in order to manage the server role "
|
||||
"menus."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/cog.py:1094
|
||||
#: src/modules/rolemenus/cog.py:1101
|
||||
#, possible-python-brace-format
|
||||
msgctxt "cmd:rolemenu_delete|error:menu_not_found"
|
||||
msgid "This server does not have a role menu called `{name}`!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/cog.py:1102
|
||||
#: src/modules/rolemenus/cog.py:1109
|
||||
#, possible-python-brace-format
|
||||
msgctxt "cmd:rolemenu_delete|confirm|title"
|
||||
msgid ""
|
||||
@@ -412,272 +417,272 @@ msgid ""
|
||||
"reversible!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/cog.py:1107
|
||||
#: src/modules/rolemenus/cog.py:1114
|
||||
msgctxt "cmd:rolemenu_delete|confirm|button:yes"
|
||||
msgid "Yes, Delete Now"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/cog.py:1112
|
||||
#: src/modules/rolemenus/cog.py:1119
|
||||
msgctxt "cmd:rolemenu_delete|confirm|button:no"
|
||||
msgid "No, Cancel"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/cog.py:1137
|
||||
#: src/modules/rolemenus/cog.py:1144
|
||||
#, possible-python-brace-format
|
||||
msgctxt "cmd:rolemenu_delete|success|desc"
|
||||
msgid "Successfully deleted the menu **{name}**"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/cog.py:1145
|
||||
#: src/modules/rolemenus/cog.py:1152
|
||||
msgctxt "cmd:rolemenu_addrole"
|
||||
msgid "addrole"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/cog.py:1148
|
||||
#: src/modules/rolemenus/cog.py:1155
|
||||
msgctxt "cmd:rolemenu_addrole|desc"
|
||||
msgid "Add a new role to an existing role menu."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/cog.py:1153
|
||||
#: src/modules/rolemenus/cog.py:1160
|
||||
msgctxt "cmd:rolemenu_addrole|param:menu"
|
||||
msgid "menu"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/cog.py:1156
|
||||
#: src/modules/rolemenus/cog.py:1163
|
||||
msgctxt "cmd:rolemenu_addrole|param:role"
|
||||
msgid "role"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/cog.py:1167
|
||||
#: src/modules/rolemenus/cog.py:1174
|
||||
msgctxt "cmd:rolemenu_addrole|param:menu|desc"
|
||||
msgid "Name of the menu to add a role to"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/cog.py:1171
|
||||
#: src/modules/rolemenus/cog.py:1178
|
||||
msgctxt "cmd:rolemenu_addrole|param:role|desc"
|
||||
msgid "Role to add to the menu"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/cog.py:1179
|
||||
#: src/modules/rolemenus/cog.py:1186
|
||||
msgctxt "cmd:rolemenu_addrole|param:duration|desc"
|
||||
msgid "Lifetime of the role after selection in minutes."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/cog.py:1227
|
||||
#: src/modules/rolemenus/cog.py:1234
|
||||
#, possible-python-brace-format
|
||||
msgctxt "cmd:rolemenu_addrole|error:menu_not_found"
|
||||
msgid "This server does not have a role menu called `{name}`!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/cog.py:1312
|
||||
#: src/modules/rolemenus/cog.py:1319
|
||||
msgctxt "cmd:rolemenu_addrole|success:create|title"
|
||||
msgid "Added Menu Role"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/cog.py:1316
|
||||
#: src/modules/rolemenus/cog.py:1323
|
||||
#, possible-python-brace-format
|
||||
msgctxt "cmd:rolemenu_addrole|success:create|desc"
|
||||
msgid "Add the role {role} to the menu **{menu}**."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/cog.py:1334
|
||||
#: src/modules/rolemenus/cog.py:1341
|
||||
msgctxt "cmd:rolemenu_addrole|success:edit|title"
|
||||
msgid "Menu Role updated"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/cog.py:1346
|
||||
#: src/modules/rolemenus/cog.py:1353
|
||||
#, possible-python-brace-format
|
||||
msgctxt "cmd:rolemenu_addrole|error:role_exists"
|
||||
msgid "The role {role} is already selectable from the menu **{menu}**"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/cog.py:1364
|
||||
#: src/modules/rolemenus/cog.py:1371
|
||||
msgctxt "cmd:rolemenu_addrole|success|error:reaction|name"
|
||||
msgid "Note"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/cog.py:1376
|
||||
#: src/modules/rolemenus/cog.py:1383
|
||||
msgctxt "cmd:rolemenu_addrole|success|button:editor|label"
|
||||
msgid "Edit Menu"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/cog.py:1393
|
||||
#: src/modules/rolemenus/cog.py:1400
|
||||
msgctxt "cmd:rolemenu_editrole"
|
||||
msgid "editrole"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/cog.py:1396
|
||||
#: src/modules/rolemenus/cog.py:1403
|
||||
msgctxt "cmd:rolemenu_editrole|desc"
|
||||
msgid "Edit role options in an existing role menu."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/cog.py:1401
|
||||
#: src/modules/rolemenus/cog.py:1408
|
||||
msgctxt "cmd:rolemenu_editrole|param:menu"
|
||||
msgid "menu"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/cog.py:1404
|
||||
#: src/modules/rolemenus/cog.py:1411
|
||||
msgctxt "cmd:rolemenu_editrole|param:menu_role"
|
||||
msgid "menu_role"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/cog.py:1407
|
||||
#: src/modules/rolemenus/cog.py:1414
|
||||
msgctxt "cmd:rolemenu_editrole|param:role"
|
||||
msgid "new_role"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/cog.py:1418
|
||||
#: src/modules/rolemenus/cog.py:1425
|
||||
msgctxt "cmd:rolemenu_editrole|param:menu|desc"
|
||||
msgid "Name of the menu to edit the role for"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/cog.py:1422
|
||||
#: src/modules/rolemenus/cog.py:1429
|
||||
msgctxt "cmd:rolemenu_editrole|param:menu_role|desc"
|
||||
msgid "Label, name, or mention of the menu role to edit."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/cog.py:1426
|
||||
#: src/modules/rolemenus/cog.py:1433
|
||||
msgctxt "cmd:rolemenu_editrole|param:role|desc"
|
||||
msgid "New server role this menu role should give."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/cog.py:1434
|
||||
#: src/modules/rolemenus/cog.py:1441
|
||||
msgctxt "cmd:rolemenu_editrole|param:duration|desc"
|
||||
msgid "Lifetime of the role after selection in minutes."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/cog.py:1475
|
||||
#: src/modules/rolemenus/cog.py:1482
|
||||
#, possible-python-brace-format
|
||||
msgctxt "cmd:rolemenu_editrole|error:menu_not_found"
|
||||
msgid "This server does not have a role menu called `{name}`!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/cog.py:1503
|
||||
#: src/modules/rolemenus/cog.py:1510
|
||||
#, possible-python-brace-format
|
||||
msgctxt "cmd:rolemenu_editrole|error:role_not_found"
|
||||
msgid "The menu **{menu}** does not have the role **{name}**"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/cog.py:1569
|
||||
#: src/modules/rolemenus/cog.py:1576
|
||||
msgctxt "cmd:rolemenu_editrole|success|title"
|
||||
msgid "Role menu role updated"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/cog.py:1584
|
||||
#: src/modules/rolemenus/cog.py:1591
|
||||
msgctxt "cmd:rolemenu_editrole|success|error:reaction|name"
|
||||
msgid "Warning!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/cog.py:1609
|
||||
#: src/modules/rolemenus/cog.py:1616
|
||||
msgctxt "cmd:rolemenu_delrole"
|
||||
msgid "delrole"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/cog.py:1612
|
||||
#: src/modules/rolemenus/cog.py:1619
|
||||
msgctxt "cmd:rolemenu_delrole|desc"
|
||||
msgid "Remove a role from a role menu."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/cog.py:1616
|
||||
#: src/modules/rolemenus/cog.py:1623
|
||||
msgctxt "cmd:rolemenu_delrole|param:menu"
|
||||
msgid "menu"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/cog.py:1617
|
||||
#: src/modules/rolemenus/cog.py:1624
|
||||
msgctxt "cmd:rolemenu_delrole|param:menu_role"
|
||||
msgid "menu_role"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/cog.py:1622
|
||||
#: src/modules/rolemenus/cog.py:1629
|
||||
msgctxt "cmd:rolemenu_delrole|param:menu|desc"
|
||||
msgid "Name of the menu to delete the role from."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/cog.py:1626
|
||||
#: src/modules/rolemenus/cog.py:1633
|
||||
msgctxt "cmd:rolemenu_delrole|param:menu_role|desc"
|
||||
msgid "Name, label, or mention of the role to delete."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/cog.py:1644
|
||||
#: src/modules/rolemenus/cog.py:1651
|
||||
msgctxt "cmd:rolemenu_delrole|error:author_perms"
|
||||
msgid ""
|
||||
"You need the `MANAGE_ROLES` permission in order to manage the server role "
|
||||
"menus."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/cog.py:1668
|
||||
#: src/modules/rolemenus/cog.py:1675
|
||||
#, possible-python-brace-format
|
||||
msgctxt "cmd:rolemenu_delrole|error:menu_not_found"
|
||||
msgid "This server does not have a role menu called `{name}`!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/cog.py:1696
|
||||
#: src/modules/rolemenus/cog.py:1703
|
||||
#, possible-python-brace-format
|
||||
msgctxt "cmd:rolemenu_delrole|error:role_not_found"
|
||||
msgid "The menu **{menu}** does not have the role **{name}**"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/cog.py:1713
|
||||
#: src/modules/rolemenus/cog.py:1720
|
||||
#, possible-python-brace-format
|
||||
msgctxt "cmd:rolemenu_delrole|success"
|
||||
msgid "The role **{name}** was successfully removed from the menu **{menu}**."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/roleoptions.py:54
|
||||
#: src/modules/rolemenus/roleoptions.py:57
|
||||
msgctxt "roleset:role"
|
||||
msgid "role"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/roleoptions.py:57
|
||||
#: src/modules/rolemenus/roleoptions.py:60
|
||||
msgctxt "roleset:role|desc"
|
||||
msgid "The role associated to this menu item."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/roleoptions.py:61
|
||||
#: src/modules/rolemenus/roleoptions.py:64
|
||||
msgctxt "roleset:role|long_desc"
|
||||
msgid "The role given when this menu item is selected in the role menu."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/roleoptions.py:74
|
||||
#: src/modules/rolemenus/roleoptions.py:77
|
||||
#, possible-python-brace-format
|
||||
msgctxt "roleset:role|set_response:set"
|
||||
msgid "This menu item will now give the role {role}."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/roleoptions.py:82
|
||||
#: src/modules/rolemenus/roleoptions.py:88
|
||||
msgctxt "roleset:label"
|
||||
msgid "label"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/roleoptions.py:85
|
||||
#: src/modules/rolemenus/roleoptions.py:91
|
||||
msgctxt "roleset:label|desc"
|
||||
msgid "A short button label for this role."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/roleoptions.py:90
|
||||
#: src/modules/rolemenus/roleoptions.py:96
|
||||
msgctxt "roleset:label|long_desc"
|
||||
msgid ""
|
||||
"A short name for this role, to be displayed in button labels, dropdown "
|
||||
"titles, and some menu layouts. By default uses the Discord role name."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/roleoptions.py:104
|
||||
#: src/modules/rolemenus/roleoptions.py:110
|
||||
#, possible-python-brace-format
|
||||
msgctxt "roleset:role|set_response"
|
||||
msgid "This menu role is now called `{value}`."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/roleoptions.py:112
|
||||
#: src/modules/rolemenus/roleoptions.py:118
|
||||
msgctxt "roleset:emoji"
|
||||
msgid "emoji"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/roleoptions.py:115
|
||||
#: src/modules/rolemenus/roleoptions.py:121
|
||||
msgctxt "roleset:emoji|desc"
|
||||
msgid "The emoji associated with this role."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/roleoptions.py:119
|
||||
#: src/modules/rolemenus/roleoptions.py:125
|
||||
msgctxt "roleset:emoji|long_desc"
|
||||
msgid ""
|
||||
"The role emoji is used for the reaction (in reaction role menus), and "
|
||||
@@ -685,110 +690,120 @@ msgid ""
|
||||
"The emoji is also displayed next to the role in most menu templates."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/roleoptions.py:157
|
||||
#: src/modules/rolemenus/roleoptions.py:165
|
||||
#, possible-python-brace-format
|
||||
msgctxt "roleset:emoji|error:test_emoji"
|
||||
msgid "The selected emoji `{emoji}` is invalid or has been deleted."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/roleoptions.py:168
|
||||
#: src/modules/rolemenus/roleoptions.py:176
|
||||
#, possible-python-brace-format
|
||||
msgctxt "roleset:emoji|set_response:set"
|
||||
msgid "The menu role emoji is now {emoji}."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/roleoptions.py:173
|
||||
#: src/modules/rolemenus/roleoptions.py:181
|
||||
msgctxt "roleset:emoji|set_response:unset"
|
||||
msgid "The menu role emoji has been removed."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/roleoptions.py:181
|
||||
#: src/modules/rolemenus/roleoptions.py:189
|
||||
msgctxt "roleset:description"
|
||||
msgid "description"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/roleoptions.py:184
|
||||
#: src/modules/rolemenus/roleoptions.py:192
|
||||
msgctxt "roleset:description|desc"
|
||||
msgid "A longer description of this role."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/roleoptions.py:189
|
||||
#: src/modules/rolemenus/roleoptions.py:197
|
||||
msgctxt "roleset:description|long_desc"
|
||||
msgid ""
|
||||
"The description is displayed under the role label in dropdown style menus. "
|
||||
"It may also be used as a substitution key in custom role selection responses."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/roleoptions.py:205
|
||||
#: src/modules/rolemenus/roleoptions.py:213
|
||||
msgctxt "roleset:description|set_response:set"
|
||||
msgid "The role description has been set."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/roleoptions.py:210
|
||||
#: src/modules/rolemenus/roleoptions.py:218
|
||||
msgctxt "roleset:description|set_response:unset"
|
||||
msgid "The role description has been removed."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/roleoptions.py:218
|
||||
#: src/modules/rolemenus/roleoptions.py:226
|
||||
msgctxt "roleset:price"
|
||||
msgid "price"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/roleoptions.py:221
|
||||
msgctxt "roleset:price|desc"
|
||||
msgid "Price of the role, in LionCoins."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/roleoptions.py:225
|
||||
msgctxt "roleset:price|long_desc"
|
||||
msgid "How much the role costs when selected, in LionCoins."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/roleoptions.py:229
|
||||
msgctxt "roleset:price|desc"
|
||||
msgid "Price of the role, in LionCoins. May be negative."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/roleoptions.py:233
|
||||
msgctxt "roleset:price|long_desc"
|
||||
msgid ""
|
||||
"How many LionCoins should be deducted from a member's account when they "
|
||||
"equip this role through this menu.\n"
|
||||
"The price may be negative, in which case the member will instead be rewarded "
|
||||
"coins when they equip the role."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/roleoptions.py:240
|
||||
msgctxt "roleset:price|accepts"
|
||||
msgid "Amount of coins that the role costs."
|
||||
msgid "Amount of coins that the role costs when equipped."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/roleoptions.py:242
|
||||
#: src/modules/rolemenus/roleoptions.py:254
|
||||
#, possible-python-brace-format
|
||||
msgctxt "roleset:price|set_response:set"
|
||||
msgid "This role will now cost {price} to equip."
|
||||
msgctxt "roleset:price|set_response:positive"
|
||||
msgid "Equipping this role will now cost {coin}**{price}**."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/roleoptions.py:247
|
||||
msgctxt "roleset:price|set_response:unset"
|
||||
msgid "This role will now be free to equip from this role menu."
|
||||
#: src/modules/rolemenus/roleoptions.py:259
|
||||
msgctxt "roleset:price|set_response:zero"
|
||||
msgid "Equipping this role is now free."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/roleoptions.py:255
|
||||
#: src/modules/rolemenus/roleoptions.py:264
|
||||
#, possible-python-brace-format
|
||||
msgctxt "roleset:price|set_response:negative"
|
||||
msgid "Equipping this role will now reward {coin}**{price}**."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/roleoptions.py:272
|
||||
msgctxt "roleset:duration"
|
||||
msgid "duration"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/roleoptions.py:258
|
||||
#: src/modules/rolemenus/roleoptions.py:275
|
||||
msgctxt "roleset:duration|desc"
|
||||
msgid "Lifetime of the role after selection"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/roleoptions.py:262
|
||||
#: src/modules/rolemenus/roleoptions.py:279
|
||||
msgctxt "roleset:duration|long_desc"
|
||||
msgid ""
|
||||
"Allows creation of 'temporary roles' which expire a given time after being "
|
||||
"equipped. Refunds will not be given upon expiry."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/roleoptions.py:267
|
||||
#: src/modules/rolemenus/roleoptions.py:284
|
||||
msgctxt "roleset:duration|notset"
|
||||
msgid "Forever."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/roleoptions.py:280
|
||||
#: src/modules/rolemenus/roleoptions.py:297
|
||||
#, possible-python-brace-format
|
||||
msgctxt "roleset:duration|set_response:set"
|
||||
msgid "This role will now expire after {duration}."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/roleoptions.py:285
|
||||
#: src/modules/rolemenus/roleoptions.py:302
|
||||
msgctxt "roleset:duration|set_response:unset"
|
||||
msgid "This role will no longer expire after being selected."
|
||||
msgstr ""
|
||||
@@ -893,25 +908,31 @@ msgctxt "rolemenu|deselect|success:refund|desc"
|
||||
msgid "You have removed **{role}**, and been refunded {coin} **{amount}**."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/rolemenu.py:540
|
||||
#: src/modules/rolemenus/rolemenu.py:541
|
||||
#, possible-python-brace-format
|
||||
msgctxt "rolemenu|deselect|success:negrefund|desc"
|
||||
msgid "You have removed **{role}**, and have lost {coin} **{amount}**."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/rolemenu.py:546
|
||||
#, possible-python-brace-format
|
||||
msgctxt "rolemenu|deselect|success:norefund|desc"
|
||||
msgid "You have unequipped **{role}**."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/rolemenu.py:554
|
||||
#: src/modules/rolemenus/rolemenu.py:560
|
||||
#, possible-python-brace-format
|
||||
msgctxt "rolemenu|select|error:required_role"
|
||||
msgid "You need to have the **{role}** role to use this!"
|
||||
msgid "You need to have the role **{role}** required to use this menu!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/rolemenu.py:568
|
||||
#: src/modules/rolemenus/rolemenu.py:574
|
||||
#, possible-python-brace-format
|
||||
msgctxt "rolemenu|select|error:max_obtainable"
|
||||
msgid "You already have the maximum of {obtainable} roles from this menu!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/rolemenu.py:582
|
||||
#: src/modules/rolemenus/rolemenu.py:588
|
||||
#, possible-python-brace-format
|
||||
msgctxt "rolemenu|select|error:insufficient_funds"
|
||||
msgid ""
|
||||
@@ -919,41 +940,41 @@ msgid ""
|
||||
"**{balance}**!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/rolemenu.py:598
|
||||
#: src/modules/rolemenus/rolemenu.py:604
|
||||
msgctxt "rolemenu|select|error:perms"
|
||||
msgid "I don't have enough permissions to give you this role!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/rolemenu.py:605
|
||||
#: src/modules/rolemenus/rolemenu.py:611
|
||||
msgctxt "rolemenu|select|error:discord"
|
||||
msgid ""
|
||||
"An unknown error occurred while assigning your role! Please try again later."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/rolemenu.py:647
|
||||
#: src/modules/rolemenus/rolemenu.py:653
|
||||
msgctxt "rolemenu|select|success|title"
|
||||
msgid "Role equipped"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/rolemenu.py:653
|
||||
#: src/modules/rolemenus/rolemenu.py:659
|
||||
#, possible-python-brace-format
|
||||
msgctxt "rolemenu|select|success:purchase|desc"
|
||||
msgid "You have purchased the role **{role}** for {coin}**{amount}**"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/rolemenu.py:658
|
||||
#: src/modules/rolemenus/rolemenu.py:664
|
||||
#, possible-python-brace-format
|
||||
msgctxt "rolemenu|select|success:nopurchase|desc"
|
||||
msgid "You have equipped the role **{role}**"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/rolemenu.py:664
|
||||
#: src/modules/rolemenus/rolemenu.py:670
|
||||
#, possible-python-brace-format
|
||||
msgctxt "rolemenu|select|expires_at"
|
||||
msgid "The role will expire at {timestamp}."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/rolemenu.py:717
|
||||
#: src/modules/rolemenus/rolemenu.py:724
|
||||
#, possible-python-brace-format
|
||||
msgctxt "rolemenu|content:reactions"
|
||||
msgid "[Click here]({jump_link}) to jump back."
|
||||
@@ -1200,251 +1221,251 @@ msgctxt "ui:menu_editor|button:bulk_edit|modal|title"
|
||||
msgid "Menu Options"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/ui/menueditor.py:215
|
||||
#: src/modules/rolemenus/ui/menueditor.py:218
|
||||
msgctxt "ui:menu_editor|button:bulk_edit|label"
|
||||
msgid "Bulk Edit"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/ui/menueditor.py:235
|
||||
#: src/modules/rolemenus/ui/menueditor.py:238
|
||||
msgctxt "ui:menu_editor|button:sticky|label"
|
||||
msgid "Toggle Sticky"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/ui/menueditor.py:259
|
||||
#: src/modules/rolemenus/ui/menueditor.py:262
|
||||
msgctxt "ui:menu_editor|button:refunds|label"
|
||||
msgid "Toggle Refunds"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/ui/menueditor.py:289
|
||||
#: src/modules/rolemenus/ui/menueditor.py:292
|
||||
msgctxt "ui:menu_editor|menu:reqroles|placeholder"
|
||||
msgid "Select Required Role"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/ui/menueditor.py:308
|
||||
#: src/modules/rolemenus/ui/menueditor.py:311
|
||||
msgctxt "ui:menu_editor|button:modify_roles|label"
|
||||
msgid "Modify Roles"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/ui/menueditor.py:337
|
||||
#: src/modules/rolemenus/ui/menueditor.py:340
|
||||
msgctxt "ui:menu_editor|role_editor|modal|title"
|
||||
msgid "Edit Menu Role"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/ui/menueditor.py:411
|
||||
#: src/modules/rolemenus/ui/menueditor.py:416
|
||||
msgctxt "ui:menu_editor|menu:add_roles|error:too_many_reactions"
|
||||
msgid "Too many roles! Reaction role menus cannot exceed `20` roles."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/ui/menueditor.py:416
|
||||
#: src/modules/rolemenus/ui/menueditor.py:421
|
||||
msgctxt "ui:menu_editor|menu:add_roles|error:too_many_roles"
|
||||
msgid "Too many roles! Role menus cannot have more than `25` roles."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/ui/menueditor.py:455
|
||||
#: src/modules/rolemenus/ui/menueditor.py:460
|
||||
msgctxt "ui:menu_editor|menu:add_roles|placeholder"
|
||||
msgid "Add Roles"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/ui/menueditor.py:481
|
||||
#: src/modules/rolemenus/ui/menueditor.py:486
|
||||
msgctxt "ui:menu_editor|menu:edit_roles|placeholder"
|
||||
msgid "Edit Roles"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/ui/menueditor.py:514
|
||||
#: src/modules/rolemenus/ui/menueditor.py:524
|
||||
msgctxt "ui:menu_editor|menu:del_role|placeholder"
|
||||
msgid "Remove Roles"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/ui/menueditor.py:538
|
||||
#: src/modules/rolemenus/ui/menueditor.py:548
|
||||
msgctxt "ui:menu_editor|button:style|error:non-managed"
|
||||
msgid ""
|
||||
"Cannot change the style of a menu attached to a message I did not send! "
|
||||
"Please repost first."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/ui/menueditor.py:551
|
||||
#: src/modules/rolemenus/ui/menueditor.py:561
|
||||
msgctxt "ui:menu_editor|button:style|label"
|
||||
msgid "Menu Style"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/ui/menueditor.py:576
|
||||
#: src/modules/rolemenus/ui/menueditor.py:586
|
||||
msgctxt "ui:menu_editor|menu:style|error:too_many_reactions"
|
||||
msgid ""
|
||||
"Too many roles! The Reaction style is limited to `20` roles (Discord "
|
||||
"limitation)."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/ui/menueditor.py:586
|
||||
#: src/modules/rolemenus/ui/menueditor.py:596
|
||||
msgctxt "ui:menu_editor|menu:style|error:incomplete_emojis"
|
||||
msgid ""
|
||||
"Cannot switch to the Reaction Role Style! Every role needs to have a "
|
||||
"distinct emoji first."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/ui/menueditor.py:602
|
||||
#: src/modules/rolemenus/ui/menueditor.py:614
|
||||
msgctxt "ui:menu_editor|menu:style|placeholder"
|
||||
msgid "Select Menu Style"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/ui/menueditor.py:606
|
||||
#: src/modules/rolemenus/ui/menueditor.py:618
|
||||
msgctxt "ui:menu_editor|menu:style|option:reaction|label"
|
||||
msgid "Reaction Roles"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/ui/menueditor.py:609
|
||||
#: src/modules/rolemenus/ui/menueditor.py:621
|
||||
msgctxt "ui:menu_editor|menu:style|option:reaction|desc"
|
||||
msgid "Roles are represented compactly as clickable reactions on a message."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/ui/menueditor.py:615
|
||||
#: src/modules/rolemenus/ui/menueditor.py:627
|
||||
msgctxt "ui:menu_editor|menu:style|option:button|label"
|
||||
msgid "Button Menu"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/ui/menueditor.py:618
|
||||
#: src/modules/rolemenus/ui/menueditor.py:630
|
||||
msgctxt "ui:menu_editor|menu:style|option:button|desc"
|
||||
msgid ""
|
||||
"Roles are represented in 5 rows of 5 buttons, each with an emoji and label."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/ui/menueditor.py:624
|
||||
#: src/modules/rolemenus/ui/menueditor.py:636
|
||||
msgctxt "ui:menu_editor|menu:style|option:dropdown|label"
|
||||
msgid "Dropdown Menu"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/ui/menueditor.py:627
|
||||
#: src/modules/rolemenus/ui/menueditor.py:639
|
||||
msgctxt "ui:menu_editor|menu:style|option:dropdown|desc"
|
||||
msgid "Roles are selectable from a dropdown menu below the message."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/ui/menueditor.py:697
|
||||
#: src/modules/rolemenus/ui/menueditor.py:709
|
||||
msgctxt "ui:menu_editor|menu:template|placeholder"
|
||||
msgid "Select Message Template"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/ui/menueditor.py:707
|
||||
#: src/modules/rolemenus/ui/menueditor.py:719
|
||||
msgctxt "ui:menu_editor|menu:template|option:custom|label"
|
||||
msgid "Custom Message"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/ui/menueditor.py:712
|
||||
#: src/modules/rolemenus/ui/menueditor.py:724
|
||||
msgctxt "ui:menu_editor|menu:template|option:custom|description"
|
||||
msgid "Entirely custom menu message (opens an interactive editor)."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/ui/menueditor.py:729
|
||||
#: src/modules/rolemenus/ui/menueditor.py:741
|
||||
msgctxt "ui:menu_editor|button:delete|confirm|title"
|
||||
msgid "Are you sure you want to delete this menu? This is not reversible!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/ui/menueditor.py:734
|
||||
#: src/modules/rolemenus/ui/menueditor.py:746
|
||||
msgctxt "ui:menu_editor|button:delete|confirm|button:yes"
|
||||
msgid "Yes, Delete Now"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/ui/menueditor.py:739
|
||||
#: src/modules/rolemenus/ui/menueditor.py:751
|
||||
msgctxt "ui:menu_editor|button:delete|confirm|button:no"
|
||||
msgid "No, Go Back"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/ui/menueditor.py:756
|
||||
#: src/modules/rolemenus/ui/menueditor.py:768
|
||||
msgctxt "ui:menu_editor|button:delete|label"
|
||||
msgid "Delete Menu"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/ui/menueditor.py:813
|
||||
#: src/modules/rolemenus/ui/menueditor.py:825
|
||||
msgctxt "ui:menu_editor|button:edit_msg|label"
|
||||
msgid "Edit Message"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/ui/menueditor.py:844
|
||||
#: src/modules/rolemenus/ui/menueditor.py:851
|
||||
msgctxt "ui:menu_editor|button:preview|label"
|
||||
msgid "Preview"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/ui/menueditor.py:860
|
||||
#: src/modules/rolemenus/ui/menueditor.py:867
|
||||
msgctxt "ui:menu_editor|button:repost|widget:repost|menu:channel|placeholder"
|
||||
msgid "Select New Channel"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/ui/menueditor.py:878
|
||||
#: src/modules/rolemenus/ui/menueditor.py:885
|
||||
msgctxt "ui:menu_editor|button:repost|widget:repost|error:perms|title"
|
||||
msgid "Insufficient Permissions!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/ui/menueditor.py:882
|
||||
#: src/modules/rolemenus/ui/menueditor.py:889
|
||||
msgctxt "ui:menu_editor|button:repost|eidget:repost|error:perms|desc"
|
||||
msgid "I lack the `EMBED_LINKS` or `SEND_MESSAGES` permission in this channel."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/ui/menueditor.py:895
|
||||
#: src/modules/rolemenus/ui/menueditor.py:902
|
||||
#, possible-python-brace-format
|
||||
msgctxt "ui:menu_editor|button:repost|widget:repost|error:post_failed"
|
||||
msgid ""
|
||||
"An error ocurred while posting to {channel}. Do I have sufficient "
|
||||
"permissions?"
|
||||
"An unknown error ocurred while posting to {channel}!\n"
|
||||
"**Error:** `{exception}`"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/ui/menueditor.py:907
|
||||
#: src/modules/rolemenus/ui/menueditor.py:915
|
||||
msgctxt "ui:menu_editor|button:repost|widget:repost|success|title"
|
||||
msgid "Role Menu Moved"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/ui/menueditor.py:913
|
||||
#: src/modules/rolemenus/ui/menueditor.py:921
|
||||
#, possible-python-brace-format
|
||||
msgctxt "ui:menu_editor|button:repost|widget:repost|success|desc:general"
|
||||
msgid "The role menu `{name}` is now available at {message_link}."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/ui/menueditor.py:927
|
||||
#: src/modules/rolemenus/ui/menueditor.py:935
|
||||
msgctxt "ui:menu_editor|button:repost|widget:repost|success|desc:reactions"
|
||||
msgid "Please check the message reactions are correct."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/ui/menueditor.py:940
|
||||
#: src/modules/rolemenus/ui/menueditor.py:948
|
||||
msgctxt "ui:menu_editor|button:repost|widget:repost|title"
|
||||
msgid "Repost Role Menu"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/ui/menueditor.py:944
|
||||
#: src/modules/rolemenus/ui/menueditor.py:952
|
||||
msgctxt "ui:menu_editor|button:repost|widget:repost|description"
|
||||
msgid "Please select the channel to which you want to resend this menu."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/ui/menueditor.py:959
|
||||
#: src/modules/rolemenus/ui/menueditor.py:967
|
||||
msgctxt "ui:menu_editor|button:repost|label:repost"
|
||||
msgid "Repost"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/ui/menueditor.py:964
|
||||
#: src/modules/rolemenus/ui/menueditor.py:972
|
||||
msgctxt "ui:menu_editor|button:repost|label:post"
|
||||
msgid "Post"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/ui/menueditor.py:974
|
||||
#: src/modules/rolemenus/ui/menueditor.py:982
|
||||
msgctxt "ui:menu_editor|embed|title"
|
||||
msgid "Role Menu Editor"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/ui/menueditor.py:983
|
||||
#: src/modules/rolemenus/ui/menueditor.py:991
|
||||
#, possible-python-brace-format
|
||||
msgctxt "ui:menu_editor|embed|description|jump_text:attached"
|
||||
msgid "Members may use this menu from {jump_url}"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/ui/menueditor.py:988
|
||||
#: src/modules/rolemenus/ui/menueditor.py:996
|
||||
msgctxt "ui:menu_editor|embed|description|jump_text:unattached"
|
||||
msgid ""
|
||||
"This menu is not currently active!\n"
|
||||
"Make it available by clicking `Post` below."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/ui/menueditor.py:1002
|
||||
#: src/modules/rolemenus/ui/menueditor.py:1010
|
||||
msgctxt "ui:menu_editor|embed|field:tips|name"
|
||||
msgid "Command Tips"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/ui/menueditor.py:1006
|
||||
#: src/modules/rolemenus/ui/menueditor.py:1014
|
||||
#, possible-python-brace-format
|
||||
msgctxt "ui:menu_editor|embed|field:tips|value"
|
||||
msgid ""
|
||||
@@ -1454,12 +1475,12 @@ msgid ""
|
||||
"{editrole} to edit role options."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/ui/menueditor.py:1046
|
||||
#: src/modules/rolemenus/ui/menueditor.py:1054
|
||||
msgctxt "ui:menu_editor|error:invald_emoji|title"
|
||||
msgid "Invalid emoji encountered."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/rolemenus/ui/menueditor.py:1050
|
||||
#: src/modules/rolemenus/ui/menueditor.py:1058
|
||||
#, possible-python-brace-format
|
||||
msgctxt "ui:menu_editor|error:invalid_emoji|desc"
|
||||
msgid ""
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-09-13 08:47+0300\n"
|
||||
"POT-Creation-Date: 2023-09-24 12:21+0300\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-09-13 08:47+0300\n"
|
||||
"POT-Creation-Date: 2023-09-24 12:21+0300\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@@ -18,25 +18,25 @@ msgstr ""
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
|
||||
|
||||
#: src/modules/schedule/cog.py:429
|
||||
#: src/modules/schedule/cog.py:478
|
||||
msgctxt "create_booking|error:no_lobby"
|
||||
msgid ""
|
||||
"This server has not set a `session_lobby`, so the scheduled session system "
|
||||
"is disabled!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/schedule/cog.py:441
|
||||
#: src/modules/schedule/cog.py:490
|
||||
msgctxt "create_booking|error:no_member"
|
||||
msgid "An unknown Discord error occurred. Please try again in a few minutes."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/schedule/cog.py:449
|
||||
#: src/modules/schedule/cog.py:498
|
||||
msgctxt "create_booking|error:blacklisted"
|
||||
msgid ""
|
||||
"You have been blacklisted from the scheduled session system in this server."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/schedule/cog.py:460
|
||||
#: src/modules/schedule/cog.py:509
|
||||
#, possible-python-brace-format
|
||||
msgctxt "create_booking|error:insufficient_balance"
|
||||
msgid ""
|
||||
@@ -48,22 +48,22 @@ msgid_plural ""
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
#: src/modules/schedule/cog.py:474
|
||||
#: src/modules/schedule/cog.py:523
|
||||
msgctxt "create_booking|error:already_booked"
|
||||
msgid "One or more requested timeslots are already booked!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/schedule/cog.py:677
|
||||
#: src/modules/schedule/cog.py:726
|
||||
msgctxt "cmd:schedule"
|
||||
msgid "schedule"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/schedule/cog.py:680
|
||||
#: src/modules/schedule/cog.py:729
|
||||
msgctxt "cmd:schedule|desc"
|
||||
msgid "View and manage your scheduled session."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/schedule/cog.py:708
|
||||
#: src/modules/schedule/cog.py:757
|
||||
#, possible-python-brace-format
|
||||
msgctxt "cmd:schedule|cancel_booking|error:parse_slot"
|
||||
msgid ""
|
||||
@@ -71,25 +71,25 @@ msgid ""
|
||||
"from the autocomplete options."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/schedule/cog.py:716
|
||||
#: src/modules/schedule/cog.py:765
|
||||
#, possible-python-brace-format
|
||||
msgctxt "cmd:schedule|cancel_booking|error:not_booked"
|
||||
msgid "Could not cancel {time} booking because it is not booked!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/schedule/cog.py:725
|
||||
#: src/modules/schedule/cog.py:774
|
||||
#, possible-python-brace-format
|
||||
msgctxt "cmd:schedule|cancel_booking|error:too_soon"
|
||||
msgid "Cannot cancel {time} booking because it is running or starting soon!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/schedule/cog.py:738
|
||||
#: src/modules/schedule/cog.py:787
|
||||
#, possible-python-brace-format
|
||||
msgctxt "cmd:schedule|cancel_booking|success"
|
||||
msgid "Successfully cancelled your booking at {time}."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/schedule/cog.py:751
|
||||
#: src/modules/schedule/cog.py:800
|
||||
#, possible-python-brace-format
|
||||
msgctxt "cmd:schedule|create_booking|error:parse_slot"
|
||||
msgid ""
|
||||
@@ -97,30 +97,30 @@ msgid ""
|
||||
"from the autocomplete options."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/schedule/cog.py:759
|
||||
#: src/modules/schedule/cog.py:808
|
||||
#, possible-python-brace-format
|
||||
msgctxt "cmd:schedule|create_booking|error:already_booked"
|
||||
msgid "You have already booked a scheduled session for {time}."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/schedule/cog.py:768
|
||||
#: src/modules/schedule/cog.py:817
|
||||
#, possible-python-brace-format
|
||||
msgctxt "cmd:schedule|create_booking|error:too_soon"
|
||||
msgid "Cannot book session at {time} because it is running or starting soon!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/schedule/cog.py:780
|
||||
#: src/modules/schedule/cog.py:829
|
||||
#, possible-python-brace-format
|
||||
msgctxt "cmd:schedule|create_booking|success"
|
||||
msgid "You have successfully scheduled a session at {time}."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/schedule/cog.py:847
|
||||
#: src/modules/schedule/cog.py:896
|
||||
msgctxt "cmd:configure_schedule"
|
||||
msgid "schedule"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/schedule/cog.py:850
|
||||
#: src/modules/schedule/cog.py:899
|
||||
msgctxt "cmd:configure_schedule|desc"
|
||||
msgid "Configure Scheduled Session system"
|
||||
msgstr ""
|
||||
@@ -841,7 +841,7 @@ msgid ""
|
||||
"`MANAGE_WEBHOOKS` permission."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/schedule/core/session.py:274
|
||||
#: src/modules/schedule/core/session.py:280
|
||||
#, possible-python-brace-format
|
||||
msgctxt "session|prepare|error:room_permissions"
|
||||
msgid ""
|
||||
@@ -850,7 +850,7 @@ msgid ""
|
||||
"`VIEW_CHANNEL` permissions."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/schedule/core/session.py:317
|
||||
#: src/modules/schedule/core/session.py:330
|
||||
#, possible-python-brace-format
|
||||
msgctxt "session|open|error:room_permissions"
|
||||
msgid ""
|
||||
@@ -859,57 +859,57 @@ msgid ""
|
||||
"`VIEW_CHANNEL` permissions."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/schedule/core/session.py:358
|
||||
#: src/modules/schedule/core/session.py:371
|
||||
#, possible-python-brace-format
|
||||
msgctxt "session|status|title"
|
||||
msgid "Session {start} - {end}"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/schedule/core/session.py:369
|
||||
#: src/modules/schedule/core/session.py:382
|
||||
msgctxt "session|status|desc:cancelled"
|
||||
msgid ""
|
||||
"I cancelled this scheduled session because I was unavailable. All members "
|
||||
"who booked the session have been refunded."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/schedule/core/session.py:376
|
||||
#: src/modules/schedule/core/session.py:389
|
||||
msgctxt "session|status|desc:no_members"
|
||||
msgid "*No members scheduled this session.*"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/schedule/core/session.py:382
|
||||
#: src/modules/schedule/core/session.py:395
|
||||
#, possible-python-brace-format
|
||||
msgctxt "session|status:preparing|desc:has_members"
|
||||
msgid "Starting {start}"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/schedule/core/session.py:385
|
||||
#: src/modules/schedule/core/session.py:398
|
||||
msgctxt "session|status:preparing|field:members"
|
||||
msgid "Members"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/schedule/core/session.py:392
|
||||
#: src/modules/schedule/core/session.py:405
|
||||
#, possible-python-brace-format
|
||||
msgctxt "session|status:running|desc:has_members"
|
||||
msgid "Finishing {start}"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/schedule/core/session.py:426
|
||||
#: src/modules/schedule/core/session.py:439
|
||||
msgctxt "session|status:running|field:waiting"
|
||||
msgid "Waiting For"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/schedule/core/session.py:432
|
||||
#: src/modules/schedule/core/session.py:445
|
||||
msgctxt "session|status:running|field:attending"
|
||||
msgid "Attending"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/schedule/core/session.py:438
|
||||
#: src/modules/schedule/core/session.py:451
|
||||
msgctxt "session|status:running|field:attended"
|
||||
msgid "Attended"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/schedule/core/session.py:463
|
||||
#: src/modules/schedule/core/session.py:476
|
||||
#, possible-python-brace-format
|
||||
msgctxt "session|status:finished|desc:everyone_att"
|
||||
msgid ""
|
||||
@@ -917,7 +917,7 @@ msgid ""
|
||||
"**{reward} + {bonus}**!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/schedule/core/session.py:474
|
||||
#: src/modules/schedule/core/session.py:487
|
||||
#, possible-python-brace-format
|
||||
msgctxt "session|status:finished|desc:some_att"
|
||||
msgid ""
|
||||
@@ -927,7 +927,7 @@ msgid ""
|
||||
"without refund!*"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/schedule/core/session.py:486
|
||||
#: src/modules/schedule/core/session.py:499
|
||||
msgctxt "session|status:finished|desc:some_att"
|
||||
msgid ""
|
||||
"No-one attended this session! No-one received rewards.\n"
|
||||
@@ -935,12 +935,12 @@ msgid ""
|
||||
"without refund!*"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/schedule/core/session.py:492
|
||||
#: src/modules/schedule/core/session.py:505
|
||||
msgctxt "session|status:finished|field:attended"
|
||||
msgid "Attended"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/schedule/core/session.py:497
|
||||
#: src/modules/schedule/core/session.py:510
|
||||
msgctxt "session|status:finished|field:missing"
|
||||
msgid "Missing"
|
||||
msgstr ""
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-09-13 08:47+0300\n"
|
||||
"POT-Creation-Date: 2023-09-24 12:21+0300\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@@ -84,91 +84,91 @@ msgctxt "settype:role|accepts"
|
||||
msgid "A role name or id"
|
||||
msgstr ""
|
||||
|
||||
#: src/settings/setting_types.py:427
|
||||
#: src/settings/setting_types.py:430
|
||||
#, possible-python-brace-format
|
||||
msgctxt "settype:role|parse|error:not_found"
|
||||
msgid "Role `{string}` could not be found in this guild!"
|
||||
msgstr ""
|
||||
|
||||
#: src/settings/setting_types.py:492
|
||||
#: src/settings/setting_types.py:495
|
||||
msgctxt "settype:bool|accepts"
|
||||
msgid "Enabled/Disabled"
|
||||
msgstr ""
|
||||
|
||||
#: src/settings/setting_types.py:497
|
||||
#: src/settings/setting_types.py:500
|
||||
msgctxt "settype:bool|parse:truthy_values"
|
||||
msgid "enabled|yes|true|on|enable|1"
|
||||
msgstr ""
|
||||
|
||||
#: src/settings/setting_types.py:501
|
||||
#: src/settings/setting_types.py:504
|
||||
msgctxt "settype:bool|parse:falsey_values"
|
||||
msgid "disabled|no|false|off|disable|0"
|
||||
msgstr ""
|
||||
|
||||
#: src/settings/setting_types.py:506
|
||||
#: src/settings/setting_types.py:509
|
||||
msgctxt "settype:bool|output:true"
|
||||
msgid "On"
|
||||
msgstr ""
|
||||
|
||||
#: src/settings/setting_types.py:507
|
||||
#: src/settings/setting_types.py:510
|
||||
msgctxt "settype:bool|output:false"
|
||||
msgid "Off"
|
||||
msgstr ""
|
||||
|
||||
#: src/settings/setting_types.py:508
|
||||
#: src/settings/setting_types.py:511
|
||||
msgctxt "settype:bool|output:none"
|
||||
msgid "Not Set"
|
||||
msgstr ""
|
||||
|
||||
#: src/settings/setting_types.py:619
|
||||
#: src/settings/setting_types.py:622
|
||||
msgctxt "settype:integer|accepts"
|
||||
msgid "An integer"
|
||||
msgstr ""
|
||||
|
||||
#: src/settings/setting_types.py:682
|
||||
#: src/settings/setting_types.py:685
|
||||
msgctxt "settype:emoji|desc"
|
||||
msgid "Unicode or custom emoji"
|
||||
msgstr ""
|
||||
|
||||
#: src/settings/setting_types.py:754
|
||||
#: src/settings/setting_types.py:757
|
||||
msgctxt "settype:guildid|accepts"
|
||||
msgid "Any Snowflake ID"
|
||||
msgstr ""
|
||||
|
||||
#: src/settings/setting_types.py:823
|
||||
#: src/settings/setting_types.py:826
|
||||
msgctxt "settype:timezone|accepts"
|
||||
msgid "A timezone name from the 'tz database' (e.g. 'Europe/London')"
|
||||
msgstr ""
|
||||
|
||||
#: src/settings/setting_types.py:893
|
||||
#: src/settings/setting_types.py:896
|
||||
msgctxt "settype:timezone|summary_table|field:supported|key"
|
||||
msgid "Supported"
|
||||
msgstr ""
|
||||
|
||||
#: src/settings/setting_types.py:897
|
||||
#: src/settings/setting_types.py:900
|
||||
#, possible-python-brace-format
|
||||
msgctxt "settype:timezone|summary_table|field:supported|value"
|
||||
msgid "Any timezone from the [tz database]({link})."
|
||||
msgstr ""
|
||||
|
||||
#: src/settings/setting_types.py:914
|
||||
#: src/settings/setting_types.py:917
|
||||
#, possible-python-brace-format
|
||||
msgctxt "set_type:timezone|acmpl|no_matching"
|
||||
msgid "No timezones matching '{input}'!"
|
||||
msgstr ""
|
||||
|
||||
#: src/settings/setting_types.py:927
|
||||
#: src/settings/setting_types.py:930
|
||||
#, possible-python-brace-format
|
||||
msgctxt "set_type:timezone|acmpl|choice"
|
||||
msgid "{tz} (Currently {now})"
|
||||
msgstr ""
|
||||
|
||||
#: src/settings/setting_types.py:957
|
||||
#: src/settings/setting_types.py:960
|
||||
msgctxt "settype:timestamp|accepts"
|
||||
msgid "A timestamp in the form YYYY-MM-DD HH:MM"
|
||||
msgstr ""
|
||||
|
||||
#: src/settings/setting_types.py:986
|
||||
#: src/settings/setting_types.py:989
|
||||
#, possible-python-brace-format
|
||||
msgctxt "settype:timestamp|parse|error:invalid"
|
||||
msgid ""
|
||||
@@ -176,43 +176,43 @@ msgid ""
|
||||
"format."
|
||||
msgstr ""
|
||||
|
||||
#: src/settings/setting_types.py:1017
|
||||
#: src/settings/setting_types.py:1020
|
||||
msgctxt "settype:raw|accepts"
|
||||
msgid "Anything"
|
||||
msgstr ""
|
||||
|
||||
#: src/settings/setting_types.py:1070
|
||||
#: src/settings/setting_types.py:1073
|
||||
msgctxt "settype:enum|accepts"
|
||||
msgid "A valid option."
|
||||
msgstr ""
|
||||
|
||||
#: src/settings/setting_types.py:1120
|
||||
#: src/settings/setting_types.py:1123
|
||||
#, possible-python-brace-format
|
||||
msgctxt "settype:enum|parse|error:not_found"
|
||||
msgid "`{provided}` is not a valid option!"
|
||||
msgstr ""
|
||||
|
||||
#: src/settings/setting_types.py:1168
|
||||
#: src/settings/setting_types.py:1171
|
||||
msgctxt "settype:duration|accepts"
|
||||
msgid "A number of days, hours, minutes, and seconds, e.g. `2d 4h 10s`."
|
||||
msgstr ""
|
||||
|
||||
#: src/settings/setting_types.py:1349
|
||||
#: src/settings/setting_types.py:1352
|
||||
msgctxt "settype:channel_list|accepts"
|
||||
msgid "Comma separated list of channel ids."
|
||||
msgstr ""
|
||||
|
||||
#: src/settings/setting_types.py:1360
|
||||
#: src/settings/setting_types.py:1363
|
||||
msgctxt "settype:role_list|accepts"
|
||||
msgid "Comma separated list of role ids."
|
||||
msgstr ""
|
||||
|
||||
#: src/settings/setting_types.py:1376
|
||||
#: src/settings/setting_types.py:1379
|
||||
msgctxt "settype:stringlist|accepts"
|
||||
msgid "Comma separated strings."
|
||||
msgstr ""
|
||||
|
||||
#: src/settings/setting_types.py:1387
|
||||
#: src/settings/setting_types.py:1390
|
||||
msgctxt "settype:guildidlist|accepts"
|
||||
msgid "Comma separated list of guild ids."
|
||||
msgstr ""
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-09-13 08:47+0300\n"
|
||||
"POT-Creation-Date: 2023-09-24 12:21+0300\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@@ -32,27 +32,27 @@ msgctxt "cmd:shop|desc"
|
||||
msgid "Purchase coloures, roles, and other goodies with LionCoins."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/shop/cog.py:124
|
||||
#: src/modules/shop/cog.py:125
|
||||
msgctxt "cmd:shop_open"
|
||||
msgid "open"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/shop/cog.py:125
|
||||
#: src/modules/shop/cog.py:126
|
||||
msgctxt "cmd:shop_open|desc"
|
||||
msgid "Open the server shop."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/shop/cog.py:151
|
||||
#: src/modules/shop/cog.py:153
|
||||
msgctxt "cmd:shop_open|error:no_shops"
|
||||
msgid "There is nothing to buy!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/shop/cog.py:213
|
||||
#: src/modules/shop/cog.py:215
|
||||
msgctxt "ui:stores|button:close|label"
|
||||
msgid "Close"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/shop/cog.py:220
|
||||
#: src/modules/shop/cog.py:222
|
||||
msgctxt "ui:stores|button:close|response|title"
|
||||
msgid "Shop Closed"
|
||||
msgstr ""
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-09-13 08:47+0300\n"
|
||||
"POT-Creation-Date: 2023-09-24 12:21+0300\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@@ -17,58 +17,58 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=CHARSET\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#: src/modules/statistics/cog.py:42
|
||||
#: src/modules/statistics/cog.py:43
|
||||
msgctxt "cmd:me"
|
||||
msgid "me"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/statistics/cog.py:45
|
||||
#: src/modules/statistics/cog.py:46
|
||||
msgctxt "cmd:me|desc"
|
||||
msgid "Display your personal profile and summary statistics."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/statistics/cog.py:55
|
||||
#: src/modules/statistics/cog.py:56
|
||||
msgctxt "cmd:stats"
|
||||
msgid "stats"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/statistics/cog.py:58
|
||||
#: src/modules/statistics/cog.py:59
|
||||
msgctxt "cmd:stats|desc"
|
||||
msgid "Weekly and monthly statistics for your recent activity."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/statistics/cog.py:71
|
||||
#: src/modules/statistics/cog.py:72
|
||||
msgctxt "cmd:leaderboard"
|
||||
msgid "leaderboard"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/statistics/cog.py:74
|
||||
#: src/modules/statistics/cog.py:75
|
||||
msgctxt "cmd:leaderboard|desc"
|
||||
msgid "Server leaderboard."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/statistics/cog.py:89
|
||||
#: src/modules/statistics/cog.py:90
|
||||
#, possible-python-brace-format
|
||||
msgctxt "cmd:leaderboard|chunking|desc"
|
||||
msgid "Requesting server member list from Discord, please wait {loading}"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/statistics/cog.py:108
|
||||
#: src/modules/statistics/cog.py:113
|
||||
msgctxt "cmd:configure_statistics"
|
||||
msgid "statistics"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/statistics/cog.py:109
|
||||
#: src/modules/statistics/cog.py:114
|
||||
msgctxt "cmd:configure_statistics|desc"
|
||||
msgid "Statistics configuration panel"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/statistics/cog.py:112
|
||||
#: src/modules/statistics/cog.py:117
|
||||
msgctxt "cmd:configure_statistics|param:season_start"
|
||||
msgid "season_start"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/statistics/cog.py:117
|
||||
#: src/modules/statistics/cog.py:122
|
||||
msgctxt "cmd:configure_statistics|param:season_start|desc"
|
||||
msgid ""
|
||||
"Time from which to start counting activity for rank badges and season "
|
||||
@@ -613,101 +613,108 @@ msgid ""
|
||||
"again to revert."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/statistics/ui/leaderboard.py:250
|
||||
#: src/modules/statistics/ui/leaderboard.py:253
|
||||
msgctxt "ui:leaderboard|menu:stats|placeholder"
|
||||
msgid "Select Activity Type"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/statistics/ui/leaderboard.py:259
|
||||
#: src/modules/statistics/ui/leaderboard.py:262
|
||||
msgctxt "ui:leaderboard|menu:stats|item:voice"
|
||||
msgid "Voice Activity"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/statistics/ui/leaderboard.py:270
|
||||
#: src/modules/statistics/ui/leaderboard.py:273
|
||||
msgctxt "ui:leaderboard|menu:stats|item:study"
|
||||
msgid "Study Statistics"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/statistics/ui/leaderboard.py:281
|
||||
#: src/modules/statistics/ui/leaderboard.py:284
|
||||
msgctxt "ui:leaderboard|menu:stats|item:message"
|
||||
msgid "Message Activity"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/statistics/ui/leaderboard.py:292
|
||||
#: src/modules/statistics/ui/leaderboard.py:295
|
||||
msgctxt "ui:leaderboard|menu;stats|item:anki"
|
||||
msgid "Anki Cards Reviewed"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/statistics/ui/leaderboard.py:346
|
||||
#: src/modules/statistics/ui/leaderboard.py:349
|
||||
msgctxt "ui:leaderboard|button:season|label"
|
||||
msgid "This Season"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/statistics/ui/leaderboard.py:350
|
||||
#: src/modules/statistics/ui/leaderboard.py:353
|
||||
msgctxt "ui:leaderboard|button:day|label"
|
||||
msgid "Today"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/statistics/ui/leaderboard.py:354
|
||||
#: src/modules/statistics/ui/leaderboard.py:357
|
||||
msgctxt "ui:leaderboard|button:week|label"
|
||||
msgid "This Week"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/statistics/ui/leaderboard.py:358
|
||||
#: src/modules/statistics/ui/leaderboard.py:361
|
||||
msgctxt "ui:leaderboard|button:month|label"
|
||||
msgid "This Month"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/statistics/ui/leaderboard.py:362
|
||||
#: src/modules/statistics/ui/leaderboard.py:365
|
||||
msgctxt "ui:leaderboard|button:alltime|label"
|
||||
msgid "All Time"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/statistics/ui/leaderboard.py:366
|
||||
#: src/modules/statistics/ui/leaderboard.py:369
|
||||
msgctxt "ui:leaderboard|button:jump|label"
|
||||
msgid "Jump"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/statistics/ui/leaderboard.py:381
|
||||
#: src/modules/statistics/ui/leaderboard.py:384
|
||||
msgctxt "ui:leaderboard|button:jump|input:title"
|
||||
msgid "Jump to page"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/statistics/ui/leaderboard.py:385
|
||||
#: src/modules/statistics/ui/leaderboard.py:388
|
||||
msgctxt "ui:leaderboard|button:jump|input:question"
|
||||
msgid "Page number to jump to"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/statistics/ui/leaderboard.py:396
|
||||
#: src/modules/statistics/ui/leaderboard.py:399
|
||||
msgctxt "ui:leaderboard|button:jump|error:invalid_page"
|
||||
msgid "Invalid page number, please try again!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/statistics/ui/leaderboard.py:442
|
||||
#: src/modules/statistics/ui/leaderboard.py:443
|
||||
msgctxt "ui:leaderboard|chunk_warning"
|
||||
msgid ""
|
||||
"**Note:** Could not retrieve member list from Discord, so some members may "
|
||||
"be missing. Try again in a minute!"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/statistics/ui/leaderboard.py:450
|
||||
#, possible-python-brace-format
|
||||
msgctxt "ui:leaderboard|since"
|
||||
msgid "Counting statistics since {timestamp}"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/statistics/ui/leaderboard.py:453
|
||||
#: src/modules/statistics/ui/leaderboard.py:463
|
||||
#, possible-python-brace-format
|
||||
msgctxt "ui:leaderboard|mode:voice|message:empty|desc"
|
||||
msgid "There has been no voice activity since {timestamp}"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/statistics/ui/leaderboard.py:458
|
||||
#: src/modules/statistics/ui/leaderboard.py:468
|
||||
#, possible-python-brace-format
|
||||
msgctxt "ui:leaderboard|mode:text|message:empty|desc"
|
||||
msgid "There has been no message activity since {timestamp}"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/statistics/ui/leaderboard.py:463
|
||||
#: src/modules/statistics/ui/leaderboard.py:473
|
||||
#, possible-python-brace-format
|
||||
msgctxt "ui:leaderboard|mode:anki|message:empty|desc"
|
||||
msgid "There have been no Anki cards reviewed since {timestamp}"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/statistics/ui/leaderboard.py:472
|
||||
#: src/modules/statistics/ui/leaderboard.py:482
|
||||
msgctxt "ui:leaderboard|message:empty|title"
|
||||
msgid "Leaderboard Empty!"
|
||||
msgstr ""
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-09-13 08:47+0300\n"
|
||||
"POT-Creation-Date: 2023-09-24 12:21+0300\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-09-13 08:47+0300\n"
|
||||
"POT-Creation-Date: 2023-09-24 12:21+0300\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-09-13 08:47+0300\n"
|
||||
"POT-Creation-Date: 2023-09-24 12:21+0300\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-09-13 08:47+0300\n"
|
||||
"POT-Creation-Date: 2023-09-24 12:21+0300\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-09-13 08:47+0300\n"
|
||||
"POT-Creation-Date: 2023-09-24 12:21+0300\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-09-13 08:47+0300\n"
|
||||
"POT-Creation-Date: 2023-09-24 12:21+0300\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-09-13 08:47+0300\n"
|
||||
"POT-Creation-Date: 2023-09-24 12:21+0300\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-09-13 08:47+0300\n"
|
||||
"POT-Creation-Date: 2023-09-24 12:21+0300\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-09-13 08:47+0300\n"
|
||||
"POT-Creation-Date: 2023-09-24 12:21+0300\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-09-13 08:47+0300\n"
|
||||
"POT-Creation-Date: 2023-09-24 12:21+0300\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-09-13 08:47+0300\n"
|
||||
"POT-Creation-Date: 2023-09-24 12:21+0300\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@@ -17,68 +17,68 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=CHARSET\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#: src/wards.py:79
|
||||
#: src/wards.py:83
|
||||
msgctxt "ward:sys_admin|failed"
|
||||
msgid "You must be a bot owner to do this!"
|
||||
msgstr ""
|
||||
|
||||
#: src/wards.py:95
|
||||
#: src/wards.py:99
|
||||
msgctxt "ward:high_management|failed"
|
||||
msgid "You must have the `ADMINISTRATOR` permission in this server to do this!"
|
||||
msgstr ""
|
||||
|
||||
#: src/wards.py:111
|
||||
#: src/wards.py:115
|
||||
msgctxt "ward:low_management|failed"
|
||||
msgid "You must have the `MANAGE_GUILD` permission in this server to do this!"
|
||||
msgstr ""
|
||||
|
||||
#: src/wards.py:123
|
||||
#: src/wards.py:127
|
||||
msgctxt "ward:moderator|failed"
|
||||
msgid ""
|
||||
"You must have the configured moderator role, or `MANAGE_GUILD` permissions "
|
||||
"to do this."
|
||||
msgstr ""
|
||||
|
||||
#: src/wards.py:149
|
||||
#: src/wards.py:153
|
||||
#, possible-python-brace-format
|
||||
msgctxt "ward:equippable_role|error:bot_managed"
|
||||
msgid "I cannot manage {role} because it is managed by another bot!"
|
||||
msgstr ""
|
||||
|
||||
#: src/wards.py:156
|
||||
#: src/wards.py:160
|
||||
#, possible-python-brace-format
|
||||
msgctxt "ward:equippable_role|error:integration"
|
||||
msgid "I cannot manage {role} because it is managed by a server integration."
|
||||
msgstr ""
|
||||
|
||||
#: src/wards.py:163
|
||||
#: src/wards.py:167
|
||||
msgctxt "ward:equippable_role|error:default_role"
|
||||
msgid "I cannot manage the server's default role."
|
||||
msgstr ""
|
||||
|
||||
#: src/wards.py:170
|
||||
#: src/wards.py:174
|
||||
msgctxt "ward:equippable_role|error:no_perms"
|
||||
msgid "I need the `MANAGE_ROLES` permission before I can manage roles!"
|
||||
msgstr ""
|
||||
|
||||
#: src/wards.py:177
|
||||
#: src/wards.py:181
|
||||
#, possible-python-brace-format
|
||||
msgctxt "ward:equippable_role|error:my_top_role"
|
||||
msgid "I cannot assign or remove {role} because it is above my top role!"
|
||||
msgstr ""
|
||||
|
||||
#: src/wards.py:184
|
||||
#: src/wards.py:188
|
||||
#, possible-python-brace-format
|
||||
msgctxt "ward:equippable_role|error:not_assignable"
|
||||
msgid "I don't have sufficient permissions to assign or remove {role}."
|
||||
msgstr ""
|
||||
|
||||
#: src/wards.py:192
|
||||
#: src/wards.py:196
|
||||
msgctxt "ward:equippable_role|error:actor_perms"
|
||||
msgid "You need the `MANAGE_ROLES` permission before you can configure roles!"
|
||||
msgstr ""
|
||||
|
||||
#: src/wards.py:199
|
||||
#: src/wards.py:203
|
||||
#, possible-python-brace-format
|
||||
msgctxt "ward:equippable_role|error:actor_top_role"
|
||||
msgid "You cannot configure {role} because it is above your top role!"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-09-13 08:47+0300\n"
|
||||
"POT-Creation-Date: 2023-09-24 12:21+0300\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
||||
@@ -3,7 +3,7 @@ cachetools==4.2.2
|
||||
configparser==5.0.2
|
||||
discord.py
|
||||
iso8601==0.1.16
|
||||
psycopg
|
||||
psycopg[pool]
|
||||
pytz==2021.1
|
||||
topggpy
|
||||
psutil
|
||||
|
||||
@@ -22,7 +22,7 @@ async def shard_snapshot():
|
||||
snap = ShardSnapshot(
|
||||
guild_count=len(bot.guilds),
|
||||
voice_count=sum(len(channel.members) for guild in bot.guilds for channel in guild.voice_channels),
|
||||
member_count=sum(len(guild.members) for guild in bot.guilds),
|
||||
member_count=sum(guild.member_count for guild in bot.guilds),
|
||||
user_count=len(set(m.id for guild in bot.guilds for m in guild.members))
|
||||
)
|
||||
return snap
|
||||
|
||||
@@ -230,14 +230,15 @@ class BabelCog(LionCog):
|
||||
supported = self.bot.translator.supported_locales
|
||||
formatted = []
|
||||
for locale in supported:
|
||||
name = locale_names.get(locale.replace('_', '-'), None)
|
||||
if name:
|
||||
localestr = f"{locale} ({t(name)})"
|
||||
names = locale_names.get(locale.replace('_', '-'), None)
|
||||
if names:
|
||||
local_name, native_name = names
|
||||
localestr = f"{native_name} ({t(local_name)})"
|
||||
else:
|
||||
localestr = locale
|
||||
formatted.append((locale, localestr))
|
||||
|
||||
matching = {item for item in formatted if partial in item[1]}
|
||||
matching = {item for item in formatted if partial in item[1] or partial in item[0]}
|
||||
if matching:
|
||||
choices = [
|
||||
appcmds.Choice(name=localestr, value=locale)
|
||||
|
||||
@@ -38,37 +38,44 @@ class LocaleMap(Enum):
|
||||
hebrew = 'he-IL'
|
||||
|
||||
|
||||
# Original Discord names
|
||||
locale_names = {
|
||||
'en-US': _p('localenames|locale:en-US', "American English"),
|
||||
'en-GB': _p('localenames|locale:en-GB', "British English"),
|
||||
'bg': _p('localenames|locale:bg', "Bulgarian"),
|
||||
'zh-CN': _p('localenames|locale:zh-CN', "Chinese"),
|
||||
'zh-TW': _p('localenames|locale:zh-TW', "Taiwan Chinese"),
|
||||
'hr': _p('localenames|locale:hr', "Croatian"),
|
||||
'cs': _p('localenames|locale:cs', "Czech"),
|
||||
'da': _p('localenames|locale:da', "Danish"),
|
||||
'nl': _p('localenames|locale:nl', "Dutch"),
|
||||
'fi': _p('localenames|locale:fi', "Finnish"),
|
||||
'fr': _p('localenames|locale:fr', "French"),
|
||||
'de': _p('localenames|locale:de', "German"),
|
||||
'el': _p('localenames|locale:el', "Greek"),
|
||||
'hi': _p('localenames|locale:hi', "Hindi"),
|
||||
'hu': _p('localenames|locale:hu', "Hungarian"),
|
||||
'it': _p('localenames|locale:it', "Italian"),
|
||||
'ja': _p('localenames|locale:ja', "Japanese"),
|
||||
'ko': _p('localenames|locale:ko', "Korean"),
|
||||
'lt': _p('localenames|locale:lt', "Lithuanian"),
|
||||
'no': _p('localenames|locale:no', "Norwegian"),
|
||||
'pl': _p('localenames|locale:pl', "Polish"),
|
||||
'pt-BR': _p('localenames|locale:pt-BR', "Brazil Portuguese"),
|
||||
'ro': _p('localenames|locale:ro', "Romanian"),
|
||||
'ru': _p('localenames|locale:ru', "Russian"),
|
||||
'es-ES': _p('localenames|locale:es-ES', "Spain Spanish"),
|
||||
'sv-SE': _p('localenames|locale:sv-SE', "Swedish"),
|
||||
'th': _p('localenames|locale:th', "Thai"),
|
||||
'tr': _p('localenames|locale:tr', "Turkish"),
|
||||
'uk': _p('localenames|locale:uk', "Ukrainian"),
|
||||
'vi': _p('localenames|locale:vi', "Vietnamese"),
|
||||
'he': _p('localenames|locale:he', "Hebrew"),
|
||||
'he-IL': _p('localenames|locale:he_IL', "Hebrew (Israel)"),
|
||||
'id': (_p('localenames|locale:id', "Indonesian"), "Bahasa Indonesia"),
|
||||
'da': (_p('localenames|locale:da', "Danish"), "Dansk"),
|
||||
'de': (_p('localenames|locale:de', "German"), "Deutsch"),
|
||||
'en-GB': (_p('localenames|locale:en-GB', "English, UK"), "English, UK"),
|
||||
'en-US': (_p('localenames|locale:en-US', "English, US"), "English, US"),
|
||||
'es-ES': (_p('localenames|locale:es-ES', "Spanish"), "Español"),
|
||||
'fr': (_p('localenames|locale:fr', "French"), "Français"),
|
||||
'hr': (_p('localenames|locale:hr', "Croatian"), "Hrvatski"),
|
||||
'it': (_p('localenames|locale:it', "Italian"), "Italiano"),
|
||||
'lt': (_p('localenames|locale:lt', "Lithuanian"), "Lietuviškai"),
|
||||
'hu': (_p('localenames|locale:hu', "Hungarian"), "Magyar"),
|
||||
'nl': (_p('localenames|locale:nl', "Dutch"), "Nederlands"),
|
||||
'no': (_p('localenames|locale:no', "Norwegian"), "Norsk"),
|
||||
'pl': (_p('localenames|locale:pl', "Polish"), "Polski"),
|
||||
'pt-BR': (_p('localenames|locale:pt-BR', "Portuguese, Brazilian"), "Português do Brasil"),
|
||||
'ro': (_p('localenames|locale:ro', "Romanian, Romania"), "Română"),
|
||||
'fi': (_p('localenames|locale:fi', "Finnish"), "Suomi"),
|
||||
'sv-SE': (_p('localenames|locale:sv-SE', "Swedish"), "Svenska"),
|
||||
'vi': (_p('localenames|locale:vi', "Vietnamese"), "Tiếng Việt"),
|
||||
'tr': (_p('localenames|locale:tr', "Turkish"), "Türkçe"),
|
||||
'cs': (_p('localenames|locale:cs', "Czech"), "Čeština"),
|
||||
'el': (_p('localenames|locale:el', "Greek"), "Ελληνικά"),
|
||||
'bg': (_p('localenames|locale:bg', "Bulgarian"), "български"),
|
||||
'ru': (_p('localenames|locale:ru', "Russian"), "Pусский"),
|
||||
'uk': (_p('localenames|locale:uk', "Ukrainian"), "Українська"),
|
||||
'hi': (_p('localenames|locale:hi', "Hindi"), "हिन्दी"),
|
||||
'th': (_p('localenames|locale:th', "Thai"), "ไทย"),
|
||||
'zh-CN': (_p('localenames|locale:zh-CN', "Chinese, China"), "中文"),
|
||||
'ja': (_p('localenames|locale:ja', "Japanese"), "日本語"),
|
||||
'zh-TW': (_p('localenames|locale:zh-TW', "Chinese, Taiwan"), "繁體中文"),
|
||||
'ko': (_p('localenames|locale:ko', "Korean"), "한국어"),
|
||||
}
|
||||
|
||||
# More names for languages not supported by Discord
|
||||
locale_names |= {
|
||||
'he': (_p('localenames|locale:he', "Hebrew"), "Hebrew"),
|
||||
'he-IL': (_p('localenames|locale:he-IL', "Hebrew"), "Hebrew"),
|
||||
'ceaser': (_p('localenames|locale:test', "Test Language"), "dfbtfs"),
|
||||
}
|
||||
|
||||
@@ -43,9 +43,9 @@ class LocaleSetting(StringSetting):
|
||||
if data is None:
|
||||
formatted = t(_p('settype:locale|formatted:unset', "Unset"))
|
||||
else:
|
||||
name = locale_names.get(data, None)
|
||||
if name:
|
||||
formatted = f"`{data} ({t(name)})`"
|
||||
if data in locale_names:
|
||||
local_name, native_name = locale_names[data]
|
||||
formatted = f"`{native_name} ({t(local_name)})`"
|
||||
else:
|
||||
formatted = f"`{data}`"
|
||||
return formatted
|
||||
|
||||
@@ -47,33 +47,43 @@ class LeoBabel(Translator):
|
||||
Initialise the gettext translators for the supported_locales.
|
||||
"""
|
||||
self.read_supported()
|
||||
missing = []
|
||||
loaded = []
|
||||
for locale in self.supported_locales:
|
||||
for domain in self.supported_domains:
|
||||
if locale == SOURCE_LOCALE:
|
||||
continue
|
||||
try:
|
||||
translator = gettext.translation(domain, "locales/", languages=[locale])
|
||||
loaded.append(f"Loaded translator for <locale: {locale}> <domain: {domain}>")
|
||||
except OSError:
|
||||
# Presume translation does not exist
|
||||
logger.warning(f"Could not load translator for supported <locale: {locale}> <domain: {domain}>")
|
||||
pass
|
||||
else:
|
||||
logger.debug(f"Loaded translator for <locale: {locale}> <domain: {domain}>")
|
||||
missing.append(f"Could not load translator for supported <locale: {locale}> <domain: {domain}>")
|
||||
translator = null
|
||||
|
||||
self.translators[locale][domain] = translator
|
||||
if missing:
|
||||
logger.warning('\n'.join(("Missing Translators:", *missing)))
|
||||
if loaded:
|
||||
logger.debug('\n'.join(("Loaded Translators:", *loaded)))
|
||||
|
||||
async def unload(self):
|
||||
self.translators.clear()
|
||||
|
||||
def get_translator(self, locale, domain):
|
||||
if locale == SOURCE_LOCALE:
|
||||
return null
|
||||
|
||||
translator = null
|
||||
elif locale in self.supported_locales and domain in self.supported_domains:
|
||||
translator = self.translators[locale].get(domain, None)
|
||||
if translator is None:
|
||||
# This should never really happen because we already loaded the supported translators
|
||||
logger.warning(
|
||||
f"Translator missing for requested <locale: {locale}> and <domain: {domain}>. Setting NullTranslator."
|
||||
f"Translator missing for supported <locale: {locale}> "
|
||||
"and <domain: {domain}>. Setting NullTranslator."
|
||||
)
|
||||
self.translators[locale][domain] = null
|
||||
translator = self.translators[locale][domain] = null
|
||||
else:
|
||||
# Unsupported
|
||||
translator = null
|
||||
return translator
|
||||
|
||||
|
||||
28
src/bot.py
28
src/bot.py
@@ -9,6 +9,7 @@ from meta import LionBot, conf, sharding, appname, shard_talk
|
||||
from meta.app import shardname
|
||||
from meta.logger import log_context, log_action_stack, setup_main_logger
|
||||
from meta.context import ctx_bot
|
||||
from meta.monitor import ComponentMonitor, StatusLevel, ComponentStatus
|
||||
|
||||
from data import Database
|
||||
|
||||
@@ -21,7 +22,7 @@ for name in conf.config.options('LOGGING_LEVELS', no_defaults=True):
|
||||
logging.getLogger(name).setLevel(conf.logging_levels[name])
|
||||
|
||||
|
||||
setup_main_logger()
|
||||
logging_queue = setup_main_logger()
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -29,6 +30,25 @@ logger = logging.getLogger(__name__)
|
||||
db = Database(conf.data['args'])
|
||||
|
||||
|
||||
async def _data_monitor() -> ComponentStatus:
|
||||
"""
|
||||
Component monitor callback for the database.
|
||||
"""
|
||||
data = {
|
||||
'stats': str(db.pool.get_stats())
|
||||
}
|
||||
if not db.pool._opened:
|
||||
level = StatusLevel.WAITING
|
||||
info = "(WAITING) Database Pool is not opened."
|
||||
elif db.pool._closed:
|
||||
level = StatusLevel.ERRORED
|
||||
info = "(ERROR) Database Pool is closed."
|
||||
else:
|
||||
level = StatusLevel.OKAY
|
||||
info = "(OK) Database Pool statistics: {stats}"
|
||||
return ComponentStatus(level, info, info, data)
|
||||
|
||||
|
||||
async def main():
|
||||
log_action_stack.set(("Initialising",))
|
||||
logger.info("Initialising StudyLion")
|
||||
@@ -69,9 +89,13 @@ async def main():
|
||||
shard_count=sharding.shard_count,
|
||||
help_command=None,
|
||||
proxy=conf.bot.get('proxy', None),
|
||||
translator=translator
|
||||
translator=translator,
|
||||
chunk_guilds_at_startup=False,
|
||||
) as lionbot:
|
||||
ctx_bot.set(lionbot)
|
||||
lionbot.system_monitor.add_component(
|
||||
ComponentMonitor('Database', _data_monitor)
|
||||
)
|
||||
try:
|
||||
log_context.set(f"APP: {appname}")
|
||||
logger.info("StudyLion initialised, starting!", extra={'action': 'Starting'})
|
||||
|
||||
@@ -49,16 +49,20 @@ class CoinSetting(IntegerSetting):
|
||||
|
||||
if num > cls._max:
|
||||
t = ctx_translator.get().t
|
||||
raise UserInputError(t(_p(
|
||||
raise UserInputError(
|
||||
t(_p(
|
||||
'settype:coin|parse|error:too_large',
|
||||
"Provided number of coins was too high!"
|
||||
))) from None
|
||||
"You cannot set this to more than {coin}**{max}**!"
|
||||
)).format(coin=conf.emojis.coin, max=cls._max)
|
||||
) from None
|
||||
elif num < cls._min:
|
||||
t = ctx_translator.get().t
|
||||
raise UserInputError(t(_p(
|
||||
'settype:coin|parse|error:too_large',
|
||||
"Provided number of coins was too low!"
|
||||
))) from None
|
||||
raise UserInputError(
|
||||
t(_p(
|
||||
'settype:coin|parse|error:too_small',
|
||||
"You cannot set this to less than {coin}**{min}**!"
|
||||
)).format(coin=conf.emojis.coin, min=cls._min)
|
||||
) from None
|
||||
|
||||
return num
|
||||
|
||||
|
||||
2
src/gui
2
src/gui
Submodule src/gui updated: b781f7f9f2...ba9ace6ced
@@ -12,6 +12,8 @@ from aiohttp import ClientSession
|
||||
|
||||
from data import Database
|
||||
from utils.lib import tabulate
|
||||
from gui.errors import RenderingException
|
||||
from babel.translator import ctx_locale
|
||||
|
||||
from .config import Conf
|
||||
from .logger import logging_context, log_context, log_action_stack, log_wrap, set_logging_context
|
||||
@@ -19,6 +21,7 @@ from .context import context
|
||||
from .LionContext import LionContext
|
||||
from .LionTree import LionTree
|
||||
from .errors import HandledException, SafeCancellation
|
||||
from .monitor import SystemMonitor, ComponentMonitor, StatusLevel, ComponentStatus
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core import CoreCog
|
||||
@@ -46,9 +49,40 @@ class LionBot(Bot):
|
||||
self.core: Optional['CoreCog'] = None
|
||||
self.translator = translator
|
||||
|
||||
self.system_monitor = SystemMonitor()
|
||||
self.monitor = ComponentMonitor('LionBot', self._monitor_status)
|
||||
self.system_monitor.add_component(self.monitor)
|
||||
|
||||
self._locks = WeakValueDictionary()
|
||||
self._running_events = set()
|
||||
|
||||
async def _monitor_status(self):
|
||||
if self.is_closed():
|
||||
level = StatusLevel.ERRORED
|
||||
info = "(ERROR) Websocket is closed"
|
||||
data = {}
|
||||
elif self.is_ws_ratelimited():
|
||||
level = StatusLevel.WAITING
|
||||
info = "(WAITING) Websocket is ratelimited"
|
||||
data = {}
|
||||
elif not self.is_ready():
|
||||
level = StatusLevel.STARTING
|
||||
info = "(STARTING) Not yet ready"
|
||||
data = {}
|
||||
else:
|
||||
level = StatusLevel.OKAY
|
||||
info = (
|
||||
"(OK) "
|
||||
"Logged in with {guild_count} guilds, "
|
||||
", websocket latency {latency}, and {events} running events."
|
||||
)
|
||||
data = {
|
||||
'guild_count': len(self.guilds),
|
||||
'latency': self.latency,
|
||||
'events': len(self._running_events),
|
||||
}
|
||||
return ComponentStatus(level, info, info, data)
|
||||
|
||||
async def setup_hook(self) -> None:
|
||||
log_context.set(f"APP: {self.application_id}")
|
||||
await self.app_ipc.connect()
|
||||
@@ -204,13 +238,23 @@ class LionBot(Bot):
|
||||
pass
|
||||
except asyncio.TimeoutError:
|
||||
pass
|
||||
except RenderingException as e:
|
||||
logger.info(f"Command failed due to RenderingException: {repr(e)}")
|
||||
embed = self.tree.rendersplat(e)
|
||||
try:
|
||||
await ctx.error_reply(embed=embed)
|
||||
except discord.HTTPException:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.exception(
|
||||
f"Caught an unknown CommandInvokeError while executing: {cmd_str}",
|
||||
extra={'action': 'BotError', 'with_ctx': True}
|
||||
)
|
||||
|
||||
error_embed = discord.Embed(title="Something went wrong!")
|
||||
error_embed = discord.Embed(
|
||||
title="Something went wrong!",
|
||||
colour=discord.Colour.dark_red()
|
||||
)
|
||||
error_embed.description = (
|
||||
"An unexpected error occurred while processing your command!\n"
|
||||
"Our development team has been notified, and the issue will be addressed soon.\n"
|
||||
@@ -225,11 +269,12 @@ class LionBot(Bot):
|
||||
details['cmd'] = f"`{ctx.command.qualified_name}`"
|
||||
if ctx.author:
|
||||
details['author'] = f"`{ctx.author.id}` -- `{ctx.author}`"
|
||||
details['locale'] = f"`{ctx_locale.get()}`"
|
||||
if ctx.guild:
|
||||
details['guild'] = f"`{ctx.guild.id}` -- `{ctx.guild.name}`"
|
||||
details['my_guild_perms'] = f"`{ctx.guild.me.guild_permissions.value}`"
|
||||
if ctx.author:
|
||||
ownerstr = ' (owner)' if ctx.author == ctx.guild.owner else ''
|
||||
ownerstr = ' (owner)' if ctx.author.id == ctx.guild.owner_id else ''
|
||||
details['author_guild_perms'] = f"`{ctx.author.guild_permissions.value}{ownerstr}`"
|
||||
if ctx.channel.type is discord.enums.ChannelType.private:
|
||||
details['channel'] = "`Direct Message`"
|
||||
@@ -246,7 +291,7 @@ class LionBot(Bot):
|
||||
|
||||
try:
|
||||
await ctx.error_reply(embed=error_embed)
|
||||
except Exception:
|
||||
except discord.HTTPException:
|
||||
pass
|
||||
finally:
|
||||
exception.original = HandledException(exception.original)
|
||||
@@ -270,3 +315,29 @@ class LionBot(Bot):
|
||||
def add_command(self, command):
|
||||
if not hasattr(command, '_placeholder_group_'):
|
||||
super().add_command(command)
|
||||
|
||||
def request_chunking_for(self, guild):
|
||||
if not guild.chunked:
|
||||
return asyncio.create_task(
|
||||
self._connection.chunk_guild(guild, wait=False, cache=True),
|
||||
name=f"Background chunkreq for {guild.id}"
|
||||
)
|
||||
|
||||
async def on_interaction(self, interaction: discord.Interaction):
|
||||
"""
|
||||
Adds the interaction author to guild cache if appropriate.
|
||||
|
||||
This gets run a little bit late, so it is possible the interaction gets handled
|
||||
without the author being in case.
|
||||
"""
|
||||
guild = interaction.guild
|
||||
user = interaction.user
|
||||
if guild is not None and user is not None and isinstance(user, discord.Member):
|
||||
if not guild.get_member(user.id):
|
||||
guild._add_member(user)
|
||||
if guild is not None and not guild.chunked:
|
||||
# Getting an interaction in the guild is a good enough reason to request chunking
|
||||
logger.info(
|
||||
f"Unchunked guild <gid: {guild.id}> requesting chunking after interaction."
|
||||
)
|
||||
self.request_chunking_for(guild)
|
||||
|
||||
@@ -6,6 +6,7 @@ from typing import Optional, TYPE_CHECKING
|
||||
import discord
|
||||
from discord.enums import ChannelType
|
||||
from discord.ext.commands import Context
|
||||
from babel.translator import ctx_locale
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .LionBot import LionBot
|
||||
@@ -35,6 +36,7 @@ FlatContext = namedtuple(
|
||||
'interaction',
|
||||
'guild',
|
||||
'author',
|
||||
'channel',
|
||||
'alias',
|
||||
'prefix',
|
||||
'failed')
|
||||
@@ -78,6 +80,7 @@ class LionContext(Context['LionBot']):
|
||||
parts['alias'] = f"\"{self.invoked_with}\""
|
||||
if self.command_failed:
|
||||
parts['failed'] = self.command_failed
|
||||
parts['locale'] = f"\"{ctx_locale.get()}\""
|
||||
|
||||
return "<LionContext: {}>".format(
|
||||
' '.join(f"{name}={value}" for name, value in parts.items())
|
||||
|
||||
@@ -8,9 +8,12 @@ from discord.enums import InteractionType
|
||||
from discord.app_commands.namespace import Namespace
|
||||
|
||||
from utils.lib import tabulate
|
||||
from gui.errors import RenderingException
|
||||
from babel.translator import ctx_locale
|
||||
|
||||
from .logger import logging_context, set_logging_context, log_wrap, log_action_stack
|
||||
from .errors import SafeCancellation
|
||||
from .config import conf
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -29,18 +32,37 @@ class LionTree(CommandTree):
|
||||
except SafeCancellation:
|
||||
# Assume this has already been handled
|
||||
pass
|
||||
except RenderingException as e:
|
||||
logger.info(f"Tree interaction failed due to rendering exception: {repr(e)}")
|
||||
embed = self.rendersplat(e)
|
||||
await self.error_reply(interaction, embed)
|
||||
except Exception:
|
||||
logger.exception(f"Unhandled exception in interaction: {interaction}", extra={'action': 'TreeError'})
|
||||
embed = self.bugsplat(interaction, error)
|
||||
await self.error_reply(interaction, embed)
|
||||
|
||||
async def error_reply(self, interaction, embed):
|
||||
if not interaction.is_expired():
|
||||
splat = self.bugsplat(interaction, error)
|
||||
try:
|
||||
if interaction.response.is_done():
|
||||
await interaction.followup.send(embed=splat, ephemeral=True)
|
||||
await interaction.followup.send(embed=embed, ephemeral=True)
|
||||
else:
|
||||
await interaction.response.send_message(embed=splat, ephemeral=True)
|
||||
await interaction.response.send_message(embed=embed, ephemeral=True)
|
||||
except discord.HTTPException:
|
||||
pass
|
||||
|
||||
def rendersplat(self, e: RenderingException):
|
||||
embed = discord.Embed(
|
||||
title="Resource Currently Unavailable!",
|
||||
description=(
|
||||
"Sorry, the graphics service is currently unavailable!\n"
|
||||
"Please try again in a few minutes.\n"
|
||||
"If the error persists, please contact our [support team]({link})"
|
||||
).format(link=conf.bot.support_guild),
|
||||
colour=discord.Colour.dark_red()
|
||||
)
|
||||
return embed
|
||||
|
||||
def bugsplat(self, interaction, e):
|
||||
error_embed = discord.Embed(title="Something went wrong!", colour=discord.Colour.red())
|
||||
error_embed.description = (
|
||||
@@ -55,13 +77,14 @@ class LionTree(CommandTree):
|
||||
details['interactiontype'] = f"`{interaction.type}`"
|
||||
if interaction.command:
|
||||
details['cmd'] = f"`{interaction.command.qualified_name}`"
|
||||
details['locale'] = f"`{ctx_locale.get()}`"
|
||||
if interaction.user:
|
||||
details['user'] = f"`{interaction.user.id}` -- `{interaction.user}`"
|
||||
if interaction.guild:
|
||||
details['guild'] = f"`{interaction.guild.id}` -- `{interaction.guild.name}`"
|
||||
details['my_guild_perms'] = f"`{interaction.guild.me.guild_permissions.value}`"
|
||||
if interaction.user:
|
||||
ownerstr = ' (owner)' if interaction.user == interaction.guild.owner else ''
|
||||
ownerstr = ' (owner)' if interaction.user.id == interaction.guild.owner_id else ''
|
||||
details['user_guild_perms'] = f"`{interaction.user.guild_permissions.value}{ownerstr}`"
|
||||
if interaction.channel.type is discord.enums.ChannelType.private:
|
||||
details['channel'] = "`Direct Message`"
|
||||
|
||||
@@ -3,7 +3,7 @@ import configparser as cfgp
|
||||
|
||||
from .args import args
|
||||
|
||||
shard_number = args.shard or 0
|
||||
shard_number = args.shard
|
||||
|
||||
class configEmoji(PartialEmoji):
|
||||
__slots__ = ('fallback',)
|
||||
@@ -87,7 +87,10 @@ class Conf:
|
||||
"emoji": configEmoji.from_str,
|
||||
}
|
||||
)
|
||||
self.config.read(configfile)
|
||||
|
||||
with open(configfile) as conff:
|
||||
# Opening with read_file mainly to ensure the file exists
|
||||
self.config.read_file(conff)
|
||||
|
||||
self.section_name = section_name if section_name in self.config else 'DEFAULT'
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ from io import StringIO
|
||||
from functools import wraps
|
||||
from contextvars import ContextVar
|
||||
|
||||
import discord
|
||||
from discord import Webhook, File
|
||||
import aiohttp
|
||||
|
||||
@@ -188,6 +189,14 @@ class LessThanFilter(logging.Filter):
|
||||
# non-zero return means we log this message
|
||||
return 1 if record.levelno < self.max_level else 0
|
||||
|
||||
class ExactLevelFilter(logging.Filter):
|
||||
def __init__(self, target_level, name=""):
|
||||
super().__init__(name)
|
||||
self.target_level = target_level
|
||||
|
||||
def filter(self, record):
|
||||
return (record.levelno == self.target_level)
|
||||
|
||||
|
||||
class ThreadFilter(logging.Filter):
|
||||
def __init__(self, thread_name):
|
||||
@@ -234,7 +243,6 @@ class ContextInjection(logging.Filter):
|
||||
logging_handler_out = logging.StreamHandler(sys.stdout)
|
||||
logging_handler_out.setLevel(logging.DEBUG)
|
||||
logging_handler_out.setFormatter(log_fmt)
|
||||
logging_handler_out.addFilter(LessThanFilter(logging.WARNING))
|
||||
logging_handler_out.addFilter(ContextInjection())
|
||||
logger.addHandler(logging_handler_out)
|
||||
log_logger.addHandler(logging_handler_out)
|
||||
@@ -297,6 +305,10 @@ class WebHookHandler(logging.StreamHandler):
|
||||
self.webhook = Webhook.from_url(self.webhook_url, session=self.session)
|
||||
|
||||
async def post(self, record):
|
||||
if record.context == 'Webhook Logger':
|
||||
# Don't livelog livelog errors
|
||||
# Otherwise we recurse and Cloudflare hates us
|
||||
return
|
||||
log_context.set("Webhook Logger")
|
||||
log_action_stack.set(("Logging",))
|
||||
log_app.set(record.app)
|
||||
@@ -363,18 +375,20 @@ class WebHookHandler(logging.StreamHandler):
|
||||
return
|
||||
except BucketFull:
|
||||
logger.warning(
|
||||
f"Live logging webhook {self.webhook.id} going too fast! "
|
||||
"Ignoring records until rate slows down."
|
||||
"Can't keep up! "
|
||||
f"Ignoring records on live-logger {self.webhook.id}."
|
||||
)
|
||||
self.ignored += 1
|
||||
return
|
||||
else:
|
||||
if self.ignored > 0:
|
||||
logger.warning(
|
||||
"Can't keep up! "
|
||||
f"{self.ignored} live logging records on webhook {self.webhook.id} skipped, continuing."
|
||||
)
|
||||
self.ignored = 0
|
||||
|
||||
try:
|
||||
if as_file or len(message) > 1900:
|
||||
with StringIO(message) as fp:
|
||||
fp.seek(0)
|
||||
@@ -385,6 +399,11 @@ class WebHookHandler(logging.StreamHandler):
|
||||
)
|
||||
else:
|
||||
await self.webhook.send(self.prefix + '\n' + message, username=log_app.get())
|
||||
except discord.HTTPException:
|
||||
logger.exception(
|
||||
"Live logger errored. Slowing down live logger."
|
||||
)
|
||||
self.bucket.fill()
|
||||
|
||||
|
||||
handlers = []
|
||||
@@ -392,9 +411,15 @@ if webhook := conf.logging['general_log']:
|
||||
handler = WebHookHandler(webhook, batch=True)
|
||||
handlers.append(handler)
|
||||
|
||||
if webhook := conf.logging['warning_log']:
|
||||
handler = WebHookHandler(webhook, prefix=conf.logging['warning_prefix'], batch=True)
|
||||
handler.addFilter(ExactLevelFilter(logging.WARNING))
|
||||
handler.setLevel(logging.WARNING)
|
||||
handlers.append(handler)
|
||||
|
||||
if webhook := conf.logging['error_log']:
|
||||
handler = WebHookHandler(webhook, prefix=conf.logging['error_prefix'], batch=True)
|
||||
handler.setLevel(logging.WARNING)
|
||||
handler.setLevel(logging.ERROR)
|
||||
handlers.append(handler)
|
||||
|
||||
if webhook := conf.logging['critical_log']:
|
||||
|
||||
139
src/meta/monitor.py
Normal file
139
src/meta/monitor.py
Normal file
@@ -0,0 +1,139 @@
|
||||
import logging
|
||||
import asyncio
|
||||
from enum import IntEnum
|
||||
from collections import deque, ChainMap
|
||||
import datetime as dt
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class StatusLevel(IntEnum):
|
||||
ERRORED = -2
|
||||
UNSURE = -1
|
||||
WAITING = 0
|
||||
STARTING = 1
|
||||
OKAY = 2
|
||||
|
||||
@property
|
||||
def symbol(self):
|
||||
return symbols[self]
|
||||
|
||||
|
||||
symbols = {
|
||||
StatusLevel.ERRORED: '🟥',
|
||||
StatusLevel.UNSURE: '🟧',
|
||||
StatusLevel.WAITING: '⬜',
|
||||
StatusLevel.STARTING: '🟫',
|
||||
StatusLevel.OKAY: '🟩',
|
||||
}
|
||||
|
||||
|
||||
class ComponentStatus:
|
||||
def __init__(self, level: StatusLevel, short_formatstr: str, long_formatstr: str, data: dict = {}):
|
||||
self.level = level
|
||||
self.short_formatstr = short_formatstr
|
||||
self.long_formatstr = long_formatstr
|
||||
self.data = data
|
||||
self.created_at = dt.datetime.now(tz=dt.timezone.utc)
|
||||
|
||||
def format_args(self):
|
||||
extra = {
|
||||
'created_at': self.created_at,
|
||||
'level': self.level,
|
||||
'symbol': self.level.symbol,
|
||||
}
|
||||
return ChainMap(extra, self.data)
|
||||
|
||||
@property
|
||||
def short(self):
|
||||
return self.short_formatstr.format(**self.format_args())
|
||||
|
||||
@property
|
||||
def long(self):
|
||||
return self.long_formatstr.format(**self.format_args())
|
||||
|
||||
|
||||
class ComponentMonitor:
|
||||
_name = None
|
||||
|
||||
def __init__(self, name=None, callback=None):
|
||||
self._callback = callback
|
||||
self.name = name or self._name
|
||||
if not self.name:
|
||||
raise ValueError("ComponentMonitor must have a name")
|
||||
|
||||
async def _make_status(self, *args, **kwargs):
|
||||
if self._callback is not None:
|
||||
return await self._callback(*args, **kwargs)
|
||||
else:
|
||||
raise NotImplementedError
|
||||
|
||||
async def status(self) -> ComponentStatus:
|
||||
try:
|
||||
status = await self._make_status()
|
||||
except Exception as e:
|
||||
logger.exception(
|
||||
f"Status callback for component '{self.name}' failed. This should not happen."
|
||||
)
|
||||
status = ComponentStatus(
|
||||
level=StatusLevel.UNSURE,
|
||||
short_formatstr="Status callback for '{name}' failed with error '{error}'",
|
||||
long_formatstr="Status callback for '{name}' failed with error '{error}'",
|
||||
data={
|
||||
'name': self.name,
|
||||
'error': repr(e)
|
||||
}
|
||||
)
|
||||
return status
|
||||
|
||||
|
||||
class SystemMonitor:
|
||||
def __init__(self):
|
||||
self.components = {}
|
||||
self.recent = deque(maxlen=10)
|
||||
|
||||
def add_component(self, component: ComponentMonitor):
|
||||
self.components[component.name] = component
|
||||
return component
|
||||
|
||||
async def request(self):
|
||||
"""
|
||||
Request status from each component.
|
||||
"""
|
||||
tasks = {
|
||||
name: asyncio.create_task(comp.status())
|
||||
for name, comp in self.components.items()
|
||||
}
|
||||
await asyncio.gather(*tasks.values())
|
||||
status = {
|
||||
name: await fut for name, fut in tasks.items()
|
||||
}
|
||||
self.recent.append(status)
|
||||
return status
|
||||
|
||||
async def _format_summary(self, status_dict: dict[str, ComponentStatus]):
|
||||
"""
|
||||
Format a one line summary from a status dict.
|
||||
"""
|
||||
freq = {level: 0 for level in StatusLevel}
|
||||
for status in status_dict.values():
|
||||
freq[status.level] += 1
|
||||
|
||||
summary = '\t'.join(f"{level.symbol} {count}" for level, count in freq.items() if count)
|
||||
return summary
|
||||
|
||||
async def _format_overview(self, status_dict: dict[str, ComponentStatus]):
|
||||
"""
|
||||
Format an overview (one line per component) from a status dict.
|
||||
"""
|
||||
lines = []
|
||||
for name, status in status_dict.items():
|
||||
lines.append(f"{status.level.symbol} {name}: {status.short}")
|
||||
summary = await self._format_summary(status_dict)
|
||||
return '\n'.join((summary, *lines))
|
||||
|
||||
async def get_summary(self):
|
||||
return await self._format_summary(await self.request())
|
||||
|
||||
async def get_overview(self):
|
||||
return await self._format_overview(await self.request())
|
||||
@@ -190,7 +190,7 @@ class Economy(LionCog):
|
||||
# First fetch the members which currently exist
|
||||
query = self.bot.core.data.Member.table.select_where(guildid=ctx.guild.id)
|
||||
query.select('userid').with_no_adapter()
|
||||
if 2 * len(targets) < len(ctx.guild.members):
|
||||
if 2 * len(targets) < ctx.guild.member_count:
|
||||
# More efficient to fetch the targets explicitly
|
||||
query.where(userid=list(targetids))
|
||||
existent_rows = await query
|
||||
|
||||
@@ -181,15 +181,17 @@ class MemberAdminCog(LionCog):
|
||||
finally:
|
||||
self._adding_roles.discard((member.guild.id, member.id))
|
||||
|
||||
@LionCog.listener('on_member_remove')
|
||||
@LionCog.listener('on_raw_member_remove')
|
||||
@log_wrap(action="Farewell")
|
||||
async def admin_member_farewell(self, member: discord.Member):
|
||||
async def admin_member_farewell(self, payload: discord.RawMemberRemoveEvent):
|
||||
# Ignore members that just joined
|
||||
if (member.guild.id, member.id) in self._adding_roles:
|
||||
guildid = payload.guild_id
|
||||
userid = payload.user.id
|
||||
if (guildid, userid) in self._adding_roles:
|
||||
return
|
||||
|
||||
# Set lion last_left, creating the lion_member if needed
|
||||
lion = await self.bot.core.lions.fetch_member(member.guild.id, member.id)
|
||||
lion = await self.bot.core.lions.fetch_member(guildid, userid)
|
||||
await lion.data.update(last_left=utc_now())
|
||||
|
||||
# Save member roles
|
||||
@@ -197,18 +199,21 @@ class MemberAdminCog(LionCog):
|
||||
self.bot.db.conn = conn
|
||||
async with conn.transaction():
|
||||
await self.data.past_roles.delete_where(
|
||||
guildid=member.guild.id,
|
||||
userid=member.id
|
||||
guildid=guildid,
|
||||
userid=userid
|
||||
)
|
||||
# Insert current member roles
|
||||
if member.roles:
|
||||
print(type(payload.user))
|
||||
if isinstance(payload.user, discord.Member) and payload.user.roles:
|
||||
member = payload.user
|
||||
await self.data.past_roles.insert_many(
|
||||
('guildid', 'userid', 'roleid'),
|
||||
*((member.guild.id, member.id, role.id) for role in member.roles)
|
||||
*((guildid, userid, role.id) for role in member.roles)
|
||||
)
|
||||
logger.debug(
|
||||
f"Stored persisting roles for member <uid:{member.id}> in <gid:{member.guild.id}>."
|
||||
f"Stored persisting roles for member <uid:{userid}> in <gid:{guildid}>."
|
||||
)
|
||||
# TODO: Event log, and include info about unchunked members
|
||||
|
||||
@LionCog.listener('on_guild_join')
|
||||
async def admin_init_guild(self, guild: discord.Guild):
|
||||
|
||||
@@ -173,7 +173,7 @@ class MemberAdminSettings(SettingGroup):
|
||||
'{guild_name}': guild.name,
|
||||
'{guild_icon}': guild.icon.url if guild.icon else member.default_avatar.url,
|
||||
'{studying_count}': str(active),
|
||||
'{member_count}': len(guild.members),
|
||||
'{member_count}': guild.member_count,
|
||||
}
|
||||
|
||||
recurse_map(
|
||||
@@ -297,7 +297,7 @@ class MemberAdminSettings(SettingGroup):
|
||||
'{guild_name}': guild.name,
|
||||
'{guild_icon}': guild.icon.url if guild.icon else member.default_avatar.url,
|
||||
'{studying_count}': str(active),
|
||||
'{member_count}': str(len(guild.members)),
|
||||
'{member_count}': str(guild.member_count),
|
||||
'{last_time}': str(last_seen or member.joined_at.timestamp()),
|
||||
}
|
||||
|
||||
|
||||
@@ -210,7 +210,7 @@ class MemberAdminUI(ConfigUI):
|
||||
t = self.bot.translator.t
|
||||
title = t(_p(
|
||||
'ui:memberadmin|embed|title',
|
||||
"Member Admin Configuration Panel"
|
||||
"Greetings and Initial Roles Panel"
|
||||
))
|
||||
embed = discord.Embed(
|
||||
title=title,
|
||||
|
||||
@@ -32,6 +32,6 @@ class MetaCog(LionCog):
|
||||
ctx.bot,
|
||||
ctx.author,
|
||||
ctx.guild,
|
||||
show_admin=await low_management(ctx.bot, ctx.author),
|
||||
show_admin=await low_management(ctx.bot, ctx.author, ctx.guild),
|
||||
)
|
||||
await ui.run(ctx.interaction)
|
||||
|
||||
@@ -10,6 +10,7 @@ from discord import app_commands as appcmds
|
||||
from meta import LionCog, LionBot, LionContext
|
||||
from meta.logger import log_wrap
|
||||
from meta.sharding import THIS_SHARD
|
||||
from meta.monitor import ComponentMonitor, ComponentStatus, StatusLevel
|
||||
from utils.lib import utc_now
|
||||
|
||||
from wards import low_management_ward
|
||||
@@ -42,12 +43,25 @@ class TimerCog(LionCog):
|
||||
self.bot = bot
|
||||
self.data = bot.db.load_registry(TimerData())
|
||||
self.settings = TimerSettings()
|
||||
self.monitor = ComponentMonitor('TimerCog', self._monitor)
|
||||
|
||||
self.timer_options = TimerOptions()
|
||||
|
||||
self.ready = False
|
||||
self.timers = defaultdict(dict)
|
||||
|
||||
async def _monitor(self):
|
||||
if not self.ready:
|
||||
level = StatusLevel.STARTING
|
||||
info = "(STARTING) Not ready. {timers} timers loaded."
|
||||
else:
|
||||
level = StatusLevel.OKAY
|
||||
info = "(OK) {timers} timers loaded."
|
||||
data = dict(timers=len(self.timers))
|
||||
return ComponentStatus(level, info, info, data)
|
||||
|
||||
async def cog_load(self):
|
||||
self.bot.system_monitor.add_component(self.monitor)
|
||||
await self.data.init()
|
||||
|
||||
self.bot.core.guild_config.register_model_setting(self.settings.PomodoroChannel)
|
||||
@@ -386,8 +400,9 @@ class TimerCog(LionCog):
|
||||
)
|
||||
else:
|
||||
# Display the timer status ephemerally
|
||||
await ctx.interaction.response.defer(thinking=True, ephemeral=True)
|
||||
status = await timer.current_status(with_notify=False, with_warnings=False)
|
||||
await ctx.reply(**status.send_args, ephemeral=True)
|
||||
await ctx.interaction.edit_original_response(**status.edit_args)
|
||||
|
||||
if error is not None:
|
||||
await ctx.reply(embed=error, ephemeral=True)
|
||||
|
||||
@@ -12,6 +12,7 @@ from utils.lib import MessageArgs, utc_now, replace_multiple
|
||||
from core.lion_guild import LionGuild
|
||||
from core.data import CoreData
|
||||
from babel.translator import ctx_locale
|
||||
from gui.errors import RenderingException
|
||||
|
||||
from . import babel, logger
|
||||
from .data import TimerData
|
||||
@@ -83,6 +84,7 @@ class Timer:
|
||||
self.destroyed = False
|
||||
|
||||
def __repr__(self):
|
||||
# TODO: Add lock status and current state and stage
|
||||
return (
|
||||
"<Timer "
|
||||
f"channelid={self.data.channelid} "
|
||||
@@ -560,19 +562,20 @@ class Timer:
|
||||
"Timer stopped! Press `Start` to restart the timer."
|
||||
)).format(channel=f"<#{self.data.channelid}>")
|
||||
|
||||
card = await get_timer_card(self.bot, self, stage)
|
||||
await card.render()
|
||||
|
||||
if (ui := self.status_view) is None:
|
||||
ui = self.status_view = TimerStatusUI(self.bot, self, self.channel)
|
||||
|
||||
await ui.refresh()
|
||||
|
||||
return MessageArgs(
|
||||
content=content,
|
||||
file=card.as_file(f"pomodoro_{self.data.channelid}.png"),
|
||||
view=ui
|
||||
)
|
||||
card = await get_timer_card(self.bot, self, stage)
|
||||
try:
|
||||
await card.render()
|
||||
file = card.as_file(f"pomodoro_{self.data.channelid}.png")
|
||||
args = MessageArgs(content=content, file=file, view=ui)
|
||||
except RenderingException:
|
||||
args = MessageArgs(content=content, view=ui)
|
||||
|
||||
return args
|
||||
|
||||
@log_wrap(action='Send Timer Status')
|
||||
async def send_status(self, delete_last=True, **kwargs):
|
||||
@@ -785,8 +788,8 @@ class Timer:
|
||||
to_next_stage = (current.end - utc_now()).total_seconds()
|
||||
|
||||
# TODO: Consider request rate and load
|
||||
if to_next_stage > 1 * 60 - drift:
|
||||
time_to_sleep = 1 * 60
|
||||
if to_next_stage > 5 * 60 - drift:
|
||||
time_to_sleep = 5 * 60
|
||||
else:
|
||||
time_to_sleep = to_next_stage
|
||||
|
||||
|
||||
@@ -286,25 +286,76 @@ class RankCog(LionCog):
|
||||
await task
|
||||
|
||||
async def _role_check(self, session_rank: SeasonRank):
|
||||
guild = self.bot.get_guild(session_rank.guildid)
|
||||
member = guild.get_member(session_rank.userid)
|
||||
"""
|
||||
Update the member's rank roles, if required.
|
||||
"""
|
||||
guildid = session_rank.guildid
|
||||
guild = self.bot.get_guild(guildid)
|
||||
|
||||
userid = session_rank.userid
|
||||
member = guild.get_member(userid)
|
||||
|
||||
if guild is not None and member is not None and guild.me.guild_permissions.manage_roles:
|
||||
ranks = await self.get_guild_ranks(guildid)
|
||||
|
||||
crank = session_rank.current_rank
|
||||
roleid = crank.roleid if crank else None
|
||||
current_roleid = crank.roleid if crank else None
|
||||
|
||||
# First gather rank roleids, note that the last_roleid is an 'honourary' roleid
|
||||
last_roleid = session_rank.rankrow.last_roleid
|
||||
if guild is not None and member is not None and roleid != last_roleid:
|
||||
new_role = guild.get_role(roleid) if roleid else None
|
||||
last_role = guild.get_role(last_roleid) if last_roleid else None
|
||||
rank_roleids = {rank.roleid for rank in ranks}
|
||||
rank_roleids.add(last_roleid)
|
||||
|
||||
# Gather member roleids
|
||||
mem_roleids = {role.id: role for role in member.roles}
|
||||
|
||||
# Calculate diffs
|
||||
to_add = guild.get_role(current_roleid) if (current_roleid not in mem_roleids) else None
|
||||
to_rm = [
|
||||
role for roleid, role in mem_roleids.items()
|
||||
if roleid in rank_roleids and roleid != current_roleid
|
||||
]
|
||||
|
||||
# Now update roles
|
||||
new_last_roleid = last_roleid
|
||||
if guild.me.guild_permissions.manage_roles:
|
||||
|
||||
# TODO: Event log here, including errors
|
||||
to_rm = [role for role in to_rm if role.is_assignable()]
|
||||
if to_rm:
|
||||
try:
|
||||
if last_role and last_role.is_assignable():
|
||||
await member.remove_roles(last_role)
|
||||
await member.remove_roles(
|
||||
*to_rm,
|
||||
reason="Removing Old Rank Roles",
|
||||
atomic=True
|
||||
)
|
||||
roleids = ', '.join(str(role.id) for role in to_rm)
|
||||
logger.info(
|
||||
f"Removed old rank roles from <uid:{userid}> in <gid:{guildid}>: {roleids}"
|
||||
)
|
||||
new_last_roleid = None
|
||||
if new_role and new_role.is_assignable():
|
||||
await member.add_roles(new_role)
|
||||
new_last_roleid = roleid
|
||||
except discord.HTTPClient:
|
||||
pass
|
||||
except discord.HTTPException:
|
||||
logger.warning(
|
||||
f"Unexpected error removing old rank roles from <uid:{member.id}> in <gid:{guild.id}>: {to_rm}",
|
||||
exc_info=True
|
||||
)
|
||||
|
||||
if to_add and to_add.is_assignable():
|
||||
try:
|
||||
await member.add_roles(
|
||||
to_add,
|
||||
reason="Rewarding Activity Rank",
|
||||
atomic=True
|
||||
)
|
||||
logger.info(
|
||||
f"Rewarded rank role <rid:{to_add.id}> to <uid:{userid}> in <gid:{guildid}>."
|
||||
)
|
||||
new_last_roleid = to_add.id
|
||||
except discord.HTTPException:
|
||||
logger.warning(
|
||||
f"Unexpected error giving <uid:{userid}> in <gid:{guildid}> their rank role <rid:{to_add.id}>",
|
||||
exc_info=True
|
||||
)
|
||||
|
||||
if new_last_roleid != last_roleid:
|
||||
await session_rank.rankrow.update(last_roleid=new_last_roleid)
|
||||
|
||||
@@ -336,23 +387,61 @@ class RankCog(LionCog):
|
||||
if member is None:
|
||||
return
|
||||
|
||||
new_role = guild.get_role(new_rank.roleid)
|
||||
if last_roleid := session_rank.rankrow.last_roleid:
|
||||
last_role = guild.get_role(last_roleid)
|
||||
else:
|
||||
last_role = None
|
||||
last_roleid = session_rank.rankrow.last_roleid
|
||||
|
||||
# Update ranks
|
||||
if guild.me.guild_permissions.manage_roles:
|
||||
# First gather rank roleids, note that the last_roleid is an 'honourary' roleid
|
||||
rank_roleids = {rank.roleid for rank in ranks}
|
||||
rank_roleids.add(last_roleid)
|
||||
|
||||
# Gather member roleids
|
||||
mem_roleids = {role.id: role for role in member.roles}
|
||||
|
||||
# Calculate diffs
|
||||
to_add = guild.get_role(new_rank.roleid) if (new_rank.roleid not in mem_roleids) else None
|
||||
to_rm = [
|
||||
role for roleid, role in mem_roleids.items()
|
||||
if roleid in rank_roleids and roleid != new_rank.roleid
|
||||
]
|
||||
|
||||
# Now update roles
|
||||
# TODO: Event log here, including errors
|
||||
to_rm = [role for role in to_rm if role.is_assignable()]
|
||||
if to_rm:
|
||||
try:
|
||||
if last_role and last_role.is_assignable():
|
||||
await member.remove_roles(last_role)
|
||||
await member.remove_roles(
|
||||
*to_rm,
|
||||
reason="Removing Old Rank Roles",
|
||||
atomic=True
|
||||
)
|
||||
roleids = ', '.join(str(role.id) for role in to_rm)
|
||||
logger.info(
|
||||
f"Removed old rank roles from <uid:{userid}> in <gid:{guildid}>: {roleids}"
|
||||
)
|
||||
last_roleid = None
|
||||
if new_role and new_role.is_assignable():
|
||||
await member.add_roles(new_role)
|
||||
last_roleid = new_role.id
|
||||
except discord.HTTPException:
|
||||
# TODO: Event log either way
|
||||
pass
|
||||
logger.warning(
|
||||
f"Unexpected error removing old rank roles from <uid:{member.id}> in <gid:{guild.id}>: {to_rm}",
|
||||
exc_info=True
|
||||
)
|
||||
|
||||
if to_add and to_add.is_assignable():
|
||||
try:
|
||||
await member.add_roles(
|
||||
to_add,
|
||||
reason="Rewarding Activity Rank",
|
||||
atomic=True
|
||||
)
|
||||
logger.info(
|
||||
f"Rewarded rank role <rid:{to_add.id}> to <uid:{userid}> in <gid:{guildid}>."
|
||||
)
|
||||
last_roleid=to_add.id
|
||||
except discord.HTTPException:
|
||||
logger.warning(
|
||||
f"Unexpected error giving <uid:{userid}> in <gid:{guildid}> their rank role <rid:{to_add.id}>",
|
||||
exc_info=True
|
||||
)
|
||||
|
||||
# Update MemberRank row
|
||||
column = {
|
||||
@@ -438,7 +527,7 @@ class RankCog(LionCog):
|
||||
required = format_stat_range(rank_type, rank.required, short=False)
|
||||
|
||||
key_map = {
|
||||
'{role_name}': role.name,
|
||||
'{role_name}': role.name if role else 'Unknown',
|
||||
'{guild_name}': guild.name,
|
||||
'{user_name}': member.name,
|
||||
'{role_id}': role.id,
|
||||
@@ -495,6 +584,7 @@ class RankCog(LionCog):
|
||||
await interaction.response.defer(thinking=False)
|
||||
ui = RankRefreshUI(self.bot, guild, callerid=interaction.user.id, timeout=None)
|
||||
await ui.send(interaction.channel)
|
||||
ui.start()
|
||||
|
||||
# Retrieve fresh rank roles
|
||||
ranks = await self.get_guild_ranks(guild.id, refresh=True)
|
||||
@@ -503,7 +593,15 @@ class RankCog(LionCog):
|
||||
|
||||
# Ensure guild is chunked
|
||||
if not guild.chunked:
|
||||
members = await guild.chunk()
|
||||
try:
|
||||
members = await asyncio.wait_for(guild.chunk(), timeout=60)
|
||||
except asyncio.TimeoutError:
|
||||
error = t(_p(
|
||||
'rank_refresh|error:cannot_chunk|desc',
|
||||
"Could not retrieve member list from Discord. Please try again later."
|
||||
))
|
||||
await ui.set_error(error)
|
||||
return
|
||||
else:
|
||||
members = guild.members
|
||||
ui.stage_members = True
|
||||
@@ -524,7 +622,7 @@ class RankCog(LionCog):
|
||||
error = t(_p(
|
||||
'rank_refresh|error:unassignable_roles|desc',
|
||||
"I have insufficient permissions to assign the following role(s):\n{roles}"
|
||||
)).format(roles='\n'.join(role.mention for role in failing)),
|
||||
)).format(roles='\n'.join(role.mention for role in failing))
|
||||
await ui.set_error(error)
|
||||
return
|
||||
|
||||
@@ -609,8 +707,8 @@ class RankCog(LionCog):
|
||||
'rank_refresh|remove_roles|small_error',
|
||||
"*Could not remove ranks from {member}*"
|
||||
)).format(member=to_remove[index][0].mention)
|
||||
self.ui.errors.append(error)
|
||||
if len(self.ui.errors) > 10:
|
||||
ui.errors.append(error)
|
||||
if len(ui.errors) > 10:
|
||||
await ui.set_error(
|
||||
t(_p(
|
||||
'rank_refresh|remove_roles|error:too_many_issues',
|
||||
@@ -644,8 +742,8 @@ class RankCog(LionCog):
|
||||
'rank_refresh|add_roles|small_error',
|
||||
"*Could not add {role} to {member}*"
|
||||
)).format(member=to_add[index][0].mention, role=to_add[index][1].mention)
|
||||
self.ui.errors.append(error)
|
||||
if len(self.ui.errors) > 10:
|
||||
ui.errors.append(error)
|
||||
if len(ui.errors) > 10:
|
||||
await ui.set_error(
|
||||
t(_p(
|
||||
'rank_refresh|add_roles|error:too_many_issues',
|
||||
|
||||
@@ -12,6 +12,7 @@ from data import ORDER
|
||||
|
||||
from utils.ui import MessageUI, Confirm
|
||||
from utils.lib import MessageArgs
|
||||
from wards import equippable_role
|
||||
from babel.translator import ctx_translator
|
||||
|
||||
from .. import babel, logger
|
||||
@@ -185,25 +186,11 @@ class RankOverviewUI(MessageUI):
|
||||
or edit an existing rank,
|
||||
or throw an error if the role is @everyone or not manageable by the client.
|
||||
"""
|
||||
|
||||
role: discord.Role = selected.values[0]
|
||||
if role >= selection.user.top_role:
|
||||
# Do not allow user to manage a role above their own top role
|
||||
t = self.bot.translator.t
|
||||
error = t(_p(
|
||||
'ui:rank_overview|menu:roles|error:above_caller',
|
||||
"You have insufficient permissions to assign {mention} as a rank role! "
|
||||
"You may only manage roles below your top role."
|
||||
)).format(mention=role.mention)
|
||||
embed = discord.Embed(
|
||||
title=t(_p(
|
||||
'ui:rank_overview|menu:roles|error:above_caller|title',
|
||||
"Insufficient permissions!"
|
||||
)),
|
||||
description=error,
|
||||
colour=discord.Colour.brand_red()
|
||||
)
|
||||
await selection.response.send_message(embed=embed, ephemeral=True)
|
||||
elif role.is_assignable():
|
||||
|
||||
if role.is_assignable():
|
||||
# Create or edit the selected role
|
||||
existing = next((rank for rank in self.ranks if rank.roleid == role.id), None)
|
||||
if existing:
|
||||
# Display and edit the given role
|
||||
@@ -216,6 +203,8 @@ class RankOverviewUI(MessageUI):
|
||||
)
|
||||
else:
|
||||
# Create new rank based on role
|
||||
# Need to check the calling author has authority to manage this role
|
||||
await equippable_role(self.bot, role, selection.user)
|
||||
await RankEditor.create_rank(
|
||||
selection,
|
||||
self.rank_type,
|
||||
@@ -380,34 +369,6 @@ class RankOverviewUI(MessageUI):
|
||||
] or [[]]
|
||||
lines = line_blocks[self.pagen]
|
||||
desc = '\n'.join(reversed(lines))
|
||||
|
||||
# Add note about season start
|
||||
note_name = t(_p(
|
||||
'ui:rank_overview|embed|field:note|name',
|
||||
"Note"
|
||||
))
|
||||
season_start = self.lguild.data.season_start
|
||||
if season_start:
|
||||
season_str = t(_p(
|
||||
'ui:rank_overview|embed|field:note|value:with_season',
|
||||
"Ranks are determined by activity since {timestamp}."
|
||||
)).format(
|
||||
timestamp=discord.utils.format_dt(season_start)
|
||||
)
|
||||
else:
|
||||
season_str = t(_p(
|
||||
'ui:rank_overview|embed|field:note|value:without_season',
|
||||
"Ranks are determined by *all-time* statistics.\n"
|
||||
"To reward ranks from a later time (e.g. to have monthly/quarterly/yearly ranks) "
|
||||
"set the `season_start` with {stats_cmd}"
|
||||
)).format(stats_cmd=self.bot.core.mention_cmd('configure statistics'))
|
||||
if self.rank_type is RankType.VOICE:
|
||||
addendum = t(_p(
|
||||
'ui:rank_overview|embed|field:note|value|voice_addendum',
|
||||
"Also note that ranks will only be updated when a member leaves a tracked voice channel! "
|
||||
"Use the **Refresh Member Ranks** button below to update all members manually."
|
||||
))
|
||||
season_str = '\n'.join((season_str, addendum))
|
||||
else:
|
||||
# No ranks, give hints about adding ranks
|
||||
desc = t(_p(
|
||||
@@ -439,6 +400,33 @@ class RankOverviewUI(MessageUI):
|
||||
description=desc
|
||||
)
|
||||
if show_note:
|
||||
# Add note about season start
|
||||
note_name = t(_p(
|
||||
'ui:rank_overview|embed|field:note|name',
|
||||
"Note"
|
||||
))
|
||||
season_start = self.lguild.data.season_start
|
||||
if season_start:
|
||||
season_str = t(_p(
|
||||
'ui:rank_overview|embed|field:note|value:with_season',
|
||||
"Ranks are determined by activity since {timestamp}."
|
||||
)).format(
|
||||
timestamp=discord.utils.format_dt(season_start)
|
||||
)
|
||||
else:
|
||||
season_str = t(_p(
|
||||
'ui:rank_overview|embed|field:note|value:without_season',
|
||||
"Ranks are determined by *all-time* statistics.\n"
|
||||
"To reward ranks from a later time (e.g. to have monthly/quarterly/yearly ranks) "
|
||||
"set the `season_start` with {stats_cmd}"
|
||||
)).format(stats_cmd=self.bot.core.mention_cmd('configure statistics'))
|
||||
if self.rank_type is RankType.VOICE:
|
||||
addendum = t(_p(
|
||||
'ui:rank_overview|embed|field:note|value|voice_addendum',
|
||||
"Also note that ranks will only be updated when a member leaves a tracked voice channel! "
|
||||
"Use the **Refresh Member Ranks** button below to update all members manually."
|
||||
))
|
||||
season_str = '\n'.join((season_str, addendum))
|
||||
embed.add_field(
|
||||
name=note_name,
|
||||
value=season_str,
|
||||
|
||||
@@ -7,6 +7,7 @@ from discord.ui.button import button, Button, ButtonStyle
|
||||
|
||||
from meta import conf, LionBot
|
||||
from core.data import RankType
|
||||
from wards import equippable_role
|
||||
|
||||
from utils.ui import MessageUI, AButton, AsComponents
|
||||
from utils.lib import MessageArgs, replace_multiple
|
||||
@@ -112,6 +113,7 @@ class RankPreviewUI(MessageUI):
|
||||
await submit.response.defer(thinking=False)
|
||||
if self.parent is not None:
|
||||
asyncio.create_task(self.parent.refresh())
|
||||
self.bot.get_cog('RankCog').flush_guild_ranks(self.guild.id)
|
||||
await self.refresh()
|
||||
|
||||
@button(label="DELETE_PLACEHOLDER", style=ButtonStyle.red)
|
||||
@@ -130,6 +132,7 @@ class RankPreviewUI(MessageUI):
|
||||
role = None
|
||||
|
||||
await self.rank.delete()
|
||||
self.bot.get_cog('RankCog').flush_guild_ranks(self.guild.id)
|
||||
|
||||
mention = role.mention if role else str(self.rank.roleid)
|
||||
|
||||
@@ -212,25 +215,13 @@ class RankPreviewUI(MessageUI):
|
||||
role: discord.Role = selected.values[0]
|
||||
await selection.response.defer(thinking=True, ephemeral=True)
|
||||
|
||||
if role >= selection.user.top_role:
|
||||
# Do not allow user to manage a role above their own top role
|
||||
error = t(_p(
|
||||
'ui:rank_preview|menu:roles|error:above_caller',
|
||||
"You have insufficient permissions to assign {mention} as a rank role! "
|
||||
"You may only manage roles below your top role."
|
||||
))
|
||||
embed = discord.Embed(
|
||||
title=t(_p(
|
||||
'ui:rank_preview|menu:roles|error:above_caller|title',
|
||||
"Insufficient permissions!"
|
||||
)),
|
||||
description=error,
|
||||
colour=discord.Colour.brand_red()
|
||||
)
|
||||
await selection.response.send_message(embed=embed, ephemeral=True)
|
||||
elif role.is_assignable():
|
||||
if role.is_assignable():
|
||||
# Update the rank role
|
||||
# Generic permission check for the new role
|
||||
await equippable_role(self.bot, role, selection.user)
|
||||
|
||||
await self.rank.update(roleid=role.id)
|
||||
self.bot.get_cog('RankCog').flush_guild_ranks(self.guild.id)
|
||||
if self.parent is not None and not self.parent.is_finished():
|
||||
asyncio.create_task(self.parent.refresh())
|
||||
await self.refresh(thinking=selection)
|
||||
|
||||
@@ -24,6 +24,9 @@ _p = babel._p
|
||||
|
||||
|
||||
class RankRefreshUI(MessageUI):
|
||||
# Cache of live rank UIs, mainly for introspection
|
||||
_running = set()
|
||||
|
||||
def __init__(self, bot: LionBot, guild: discord.Guild, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self.bot = bot
|
||||
@@ -64,13 +67,18 @@ class RankRefreshUI(MessageUI):
|
||||
def poke(self):
|
||||
self._wakeup.set()
|
||||
|
||||
def start(self):
|
||||
self._loop_task = asyncio.create_task(self._refresh_loop(), name='Rank RefreshUI Monitor')
|
||||
self._running.add(self)
|
||||
|
||||
async def run(self, *args, **kwargs):
|
||||
await super().run(*args, **kwargs)
|
||||
self._loop_task = asyncio.create_task(self._refresh_loop(), name='refresh ui loop')
|
||||
self.start()
|
||||
|
||||
async def cleanup(self):
|
||||
if self._loop_task and not self._loop_task.done():
|
||||
self._loop_task.cancel()
|
||||
self._running.discard(self)
|
||||
await super().cleanup()
|
||||
|
||||
def progress_bar(self, value, minimum, maximum, width=10) -> str:
|
||||
@@ -104,12 +112,13 @@ class RankRefreshUI(MessageUI):
|
||||
# Join all the sections together and return
|
||||
return ''.join(bar)
|
||||
|
||||
@log_wrap('refresh ui loop')
|
||||
@log_wrap(action='refresh ui loop')
|
||||
async def _refresh_loop(self):
|
||||
while True:
|
||||
try:
|
||||
await asyncio.sleep(1)
|
||||
await asyncio.sleep(5)
|
||||
await self._wakeup.wait()
|
||||
self._wakeup.clear()
|
||||
await self.refresh()
|
||||
except asyncio.CancelledError:
|
||||
break
|
||||
|
||||
@@ -971,14 +971,21 @@ class RoleMenuCog(LionCog):
|
||||
)
|
||||
# TODO: Generate the custom message from the template if it doesn't exist
|
||||
|
||||
# TODO: Pathway for setting menu style
|
||||
|
||||
if rawmessage is not None:
|
||||
msg_config = target.config.rawmessage
|
||||
content = await msg_config.download_attachment(rawmessage)
|
||||
data = await msg_config._parse_string(content)
|
||||
data = await msg_config._parse_string(0, content)
|
||||
update_args[msg_config._column] = data
|
||||
if template is None:
|
||||
update_args[self.data.RoleMenu.templateid.name] = None
|
||||
ack_lines.append(msg_config.update_message)
|
||||
ack_lines.append(
|
||||
t(_p(
|
||||
'cmd:rolemenu_edit|parse:custom_message|success',
|
||||
"Custom menu message updated."
|
||||
))
|
||||
)
|
||||
|
||||
# Update the data, if applicable
|
||||
if update_args:
|
||||
@@ -1185,7 +1192,7 @@ class RoleMenuCog(LionCog):
|
||||
label: Optional[appcmds.Range[str, 1, 100]] = None,
|
||||
emoji: Optional[appcmds.Range[str, 0, 100]] = None,
|
||||
description: Optional[appcmds.Range[str, 0, 100]] = None,
|
||||
price: Optional[appcmds.Range[int, 0, MAX_COINS]] = None,
|
||||
price: Optional[appcmds.Range[int, -MAX_COINS, MAX_COINS]] = None,
|
||||
duration: Optional[Transform[int, DurationTransformer(60)]] = None,
|
||||
):
|
||||
# Type checking guards
|
||||
@@ -1356,7 +1363,7 @@ class RoleMenuCog(LionCog):
|
||||
await target.update_message()
|
||||
if target_is_reaction:
|
||||
try:
|
||||
await self.menu.update_reactons()
|
||||
await target.update_reactons()
|
||||
except SafeCancellation as e:
|
||||
embed.add_field(
|
||||
name=t(_p(
|
||||
@@ -1441,7 +1448,7 @@ class RoleMenuCog(LionCog):
|
||||
label: Optional[appcmds.Range[str, 1, 100]] = None,
|
||||
emoji: Optional[appcmds.Range[str, 0, 100]] = None,
|
||||
description: Optional[appcmds.Range[str, 0, 100]] = None,
|
||||
price: Optional[appcmds.Range[int, 0, MAX_COINS]] = None,
|
||||
price: Optional[appcmds.Range[int, -MAX_COINS, MAX_COINS]] = None,
|
||||
duration: Optional[Transform[int, DurationTransformer(60)]] = None,
|
||||
):
|
||||
# Type checking wards
|
||||
|
||||
@@ -165,7 +165,7 @@ class RoleMenu:
|
||||
await menu.attach()
|
||||
return menu
|
||||
|
||||
async def fetch_message(self, refresh=False):
|
||||
async def fetch_message(self, refresh=False) -> Optional[discord.Message]:
|
||||
"""
|
||||
Fetch the message the menu is attached to.
|
||||
"""
|
||||
@@ -529,11 +529,17 @@ class RoleMenu:
|
||||
"Role removed"
|
||||
))
|
||||
)
|
||||
if total_refund:
|
||||
if total_refund > 0:
|
||||
embed.description = t(_p(
|
||||
'rolemenu|deselect|success:refund|desc',
|
||||
"You have removed **{role}**, and been refunded {coin} **{amount}**."
|
||||
)).format(role=role.name, coin=self.bot.config.emojis.coin, amount=total_refund)
|
||||
if total_refund < 0:
|
||||
# TODO: Consider disallowing them from removing roles if their balance would go negative
|
||||
embed.description = t(_p(
|
||||
'rolemenu|deselect|success:negrefund|desc',
|
||||
"You have removed **{role}**, and have lost {coin} **{amount}**."
|
||||
)).format(role=role.name, coin=self.bot.config.emojis.coin, amount=-total_refund)
|
||||
else:
|
||||
embed.description = t(_p(
|
||||
'rolemenu|deselect|success:norefund|desc',
|
||||
@@ -551,7 +557,7 @@ class RoleMenu:
|
||||
raise UserInputError(
|
||||
t(_p(
|
||||
'rolemenu|select|error:required_role',
|
||||
"You need to have the **{role}** role to use this!"
|
||||
"You need to have the role **{role}** required to use this menu!"
|
||||
)).format(role=name)
|
||||
)
|
||||
|
||||
@@ -647,7 +653,7 @@ class RoleMenu:
|
||||
"Role equipped"
|
||||
))
|
||||
)
|
||||
if price > 0:
|
||||
if price:
|
||||
embed.description = t(_p(
|
||||
'rolemenu|select|success:purchase|desc',
|
||||
"You have purchased the role **{role}** for {coin}**{amount}**"
|
||||
@@ -665,6 +671,7 @@ class RoleMenu:
|
||||
)).format(
|
||||
timestamp=discord.utils.format_dt(expiry)
|
||||
)
|
||||
# TODO Event logging
|
||||
return embed
|
||||
|
||||
async def interactive_selection(self, interaction: discord.Interaction, menuroleid: int):
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
from typing import Optional
|
||||
import discord
|
||||
|
||||
from settings import ModelData
|
||||
from settings.groups import SettingGroup, ModelConfig, SettingDotDict
|
||||
from settings.setting_types import (
|
||||
RoleSetting, BoolSetting, StringSetting, DurationSetting, EmojiSetting
|
||||
RoleSetting, StringSetting, DurationSetting, EmojiSetting
|
||||
)
|
||||
from core.setting_types import CoinSetting
|
||||
from utils.ui import AButton, AsComponents
|
||||
from meta.errors import UserInputError
|
||||
from meta import conf
|
||||
from babel.translator import ctx_translator
|
||||
from constants import MAX_COINS
|
||||
|
||||
from .data import RoleMenuData
|
||||
from . import babel
|
||||
@@ -74,6 +77,9 @@ class RoleMenuRoleOptions(SettingGroup):
|
||||
"This menu item will now give the role {role}."
|
||||
)).format(role=self.formatted)
|
||||
return resp
|
||||
else:
|
||||
raise ValueError("Attempt to call update_message without a value.")
|
||||
|
||||
|
||||
@RoleMenuRoleConfig.register_model_setting
|
||||
class Label(ModelData, StringSetting):
|
||||
@@ -138,7 +144,9 @@ class RoleMenuRoleOptions(SettingGroup):
|
||||
return button
|
||||
|
||||
@classmethod
|
||||
async def _parse_string(cls, parent_id, string: str, interaction: discord.Interaction = None, **kwargs):
|
||||
async def _parse_string(cls, parent_id, string: str,
|
||||
interaction: Optional[discord.Interaction] = None,
|
||||
**kwargs):
|
||||
emojistr = await super()._parse_string(parent_id, string, interaction=interaction, **kwargs)
|
||||
if emojistr and interaction is not None:
|
||||
# Use the interaction to test
|
||||
@@ -151,7 +159,7 @@ class RoleMenuRoleOptions(SettingGroup):
|
||||
view=view,
|
||||
)
|
||||
except discord.HTTPException:
|
||||
t = interaction.client.translator.t
|
||||
t = ctx_translator.get().t
|
||||
raise UserInputError(t(_p(
|
||||
'roleset:emoji|error:test_emoji',
|
||||
"The selected emoji `{emoji}` is invalid or has been deleted."
|
||||
@@ -218,34 +226,43 @@ class RoleMenuRoleOptions(SettingGroup):
|
||||
_display_name = _p('roleset:price', "price")
|
||||
_desc = _p(
|
||||
'roleset:price|desc',
|
||||
"Price of the role, in LionCoins."
|
||||
"Price of the role, in LionCoins. May be negative."
|
||||
)
|
||||
_long_desc = _p(
|
||||
'roleset:price|long_desc',
|
||||
"How much the role costs when selected, in LionCoins."
|
||||
"How many LionCoins should be deducted from a member's account "
|
||||
"when they equip this role through this menu.\n"
|
||||
"The price may be negative, in which case the member will instead be rewarded "
|
||||
"coins when they equip the role."
|
||||
)
|
||||
_accepts = _p(
|
||||
'roleset:price|accepts',
|
||||
"Amount of coins that the role costs."
|
||||
"Amount of coins that the role costs when equipped."
|
||||
)
|
||||
_default = 0
|
||||
_min = - MAX_COINS
|
||||
_model = RoleMenuData.RoleMenuRole
|
||||
_column = RoleMenuData.RoleMenuRole.price.name
|
||||
|
||||
@property
|
||||
def update_message(self) -> str:
|
||||
t = ctx_translator.get().t
|
||||
value = self.value
|
||||
if value:
|
||||
value = self.value or 0
|
||||
if value > 0:
|
||||
resp = t(_p(
|
||||
'roleset:price|set_response:set',
|
||||
"This role will now cost {price} to equip."
|
||||
)).format(price=self.formatted)
|
||||
'roleset:price|set_response:positive',
|
||||
"Equipping this role will now cost {coin}**{price}**."
|
||||
)).format(price=value, coin=conf.emojis.coin)
|
||||
elif value == 0:
|
||||
resp = t(_p(
|
||||
'roleset:price|set_response:zero',
|
||||
"Equipping this role is now free."
|
||||
))
|
||||
else:
|
||||
resp = t(_p(
|
||||
'roleset:price|set_response:unset',
|
||||
"This role will now be free to equip from this role menu."
|
||||
))
|
||||
'roleset:price|set_response:negative',
|
||||
"Equipping this role will now reward {coin}**{price}**."
|
||||
)).format(price=-value, coin=conf.emojis.coin)
|
||||
return resp
|
||||
|
||||
@RoleMenuRoleConfig.register_model_setting
|
||||
|
||||
@@ -190,7 +190,10 @@ class MenuEditor(MessageUI):
|
||||
if not userstr:
|
||||
new_data = None
|
||||
else:
|
||||
new_data = await instance._parse_string(instance.parent_id, userstr)
|
||||
new_data = await instance._parse_string(
|
||||
instance.parent_id, userstr,
|
||||
guildid=self.menu.data.guildid
|
||||
)
|
||||
instance.data = new_data
|
||||
modified.append(instance)
|
||||
if modified:
|
||||
@@ -349,7 +352,9 @@ class MenuEditor(MessageUI):
|
||||
if not userstr:
|
||||
new_data = None
|
||||
else:
|
||||
new_data = await instance._parse_string(instance.parent_id, userstr, interaction=interaction)
|
||||
new_data = await instance._parse_string(
|
||||
instance.parent_id, userstr, interaction=interaction
|
||||
)
|
||||
instance.data = new_data
|
||||
modified.append(instance)
|
||||
if modified:
|
||||
@@ -503,6 +508,11 @@ class MenuEditor(MessageUI):
|
||||
await self.refresh(thinking=selection)
|
||||
await self.update_preview()
|
||||
await self.menu.update_message()
|
||||
if self.menu.data.menutype is MenuType.REACTION:
|
||||
try:
|
||||
await self.menu.update_reactons()
|
||||
except SafeCancellation:
|
||||
pass
|
||||
else:
|
||||
await selection.response.defer(thinking=False)
|
||||
|
||||
@@ -591,6 +601,8 @@ class MenuEditor(MessageUI):
|
||||
await self.refresh(thinking=selection)
|
||||
await self.update_preview()
|
||||
await self.menu.update_message()
|
||||
if menutype is MenuType.REACTION:
|
||||
await self.menu.update_reactons()
|
||||
else:
|
||||
await selection.response.defer()
|
||||
|
||||
@@ -644,7 +656,7 @@ class MenuEditor(MessageUI):
|
||||
self.bot, json.loads(self.menu.data.rawmessage), callback=self._editor_callback, callerid=self._callerid
|
||||
)
|
||||
self._slaves.append(editor)
|
||||
await editor.run(interaction)
|
||||
await editor.run(interaction, ephemeral=True)
|
||||
|
||||
# Template/Custom Menu
|
||||
@select(cls=Select, placeholder="TEMPLATE_MENU_PLACEHOLDER", min_values=1, max_values=1)
|
||||
@@ -821,20 +833,15 @@ class MenuEditor(MessageUI):
|
||||
"""
|
||||
Display or update the preview message.
|
||||
"""
|
||||
args = await self.menu.make_args()
|
||||
view = await self.menu.make_view()
|
||||
if self._preview is not None:
|
||||
try:
|
||||
await self._preview.delete_original_response()
|
||||
except discord.HTTPException:
|
||||
pass
|
||||
self._preview = None
|
||||
await press.response.send_message(
|
||||
**args.send_args,
|
||||
view=view or discord.utils.MISSING,
|
||||
ephemeral=True
|
||||
)
|
||||
await press.response.defer(thinking=True, ephemeral=True)
|
||||
self._preview = press
|
||||
await self.update_preview()
|
||||
|
||||
async def preview_button_refresh(self):
|
||||
t = self.bot.translator.t
|
||||
@@ -887,13 +894,14 @@ class MenuEditor(MessageUI):
|
||||
description=desc
|
||||
)
|
||||
await selection.edit_original_response(embed=embed)
|
||||
except discord.HTTPException:
|
||||
except discord.HTTPException as e:
|
||||
error = discord.Embed(
|
||||
colour=discord.Colour.brand_red(),
|
||||
description=t(_p(
|
||||
'ui:menu_editor|button:repost|widget:repost|error:post_failed',
|
||||
"An error ocurred while posting to {channel}. Do I have sufficient permissions?"
|
||||
)).format(channel=channel.mention)
|
||||
"An unknown error ocurred while posting to {channel}!\n"
|
||||
"**Error:** `{exception}`"
|
||||
)).format(channel=channel.mention, exception=e.text)
|
||||
)
|
||||
await selection.edit_original_response(embed=error)
|
||||
else:
|
||||
@@ -948,7 +956,7 @@ class MenuEditor(MessageUI):
|
||||
title=title, description=desc
|
||||
)
|
||||
# Send as response with the repost widget attached
|
||||
await press.response.send_message(embed=embed, view=AsComponents(repost_widget))
|
||||
await press.response.send_message(embed=embed, view=AsComponents(repost_widget), ephemeral=True)
|
||||
|
||||
async def repost_button_refresh(self):
|
||||
t = self.bot.translator.t
|
||||
@@ -1039,7 +1047,7 @@ class MenuEditor(MessageUI):
|
||||
role_index = int(splits[i-1])
|
||||
mrole = self.menu.roles[role_index]
|
||||
|
||||
error = discord.Embed(
|
||||
embed = discord.Embed(
|
||||
colour=discord.Colour.brand_red(),
|
||||
title=t(_p(
|
||||
'ui:menu_editor|error:invald_emoji|title',
|
||||
@@ -1051,7 +1059,7 @@ class MenuEditor(MessageUI):
|
||||
)).format(emoji=mrole.config.emoji.data, label=mrole.config.label.data)
|
||||
)
|
||||
await mrole.data.update(emoji=None)
|
||||
await self.channel.send(embed=error)
|
||||
await self.channel.send(embed=embed)
|
||||
|
||||
async def _redraw(self, args):
|
||||
try:
|
||||
|
||||
@@ -13,6 +13,7 @@ from meta import LionCog, LionBot, LionContext
|
||||
from meta.logger import log_wrap
|
||||
from meta.errors import UserInputError, ResponseTimedOut
|
||||
from meta.sharding import THIS_SHARD
|
||||
from meta.monitor import ComponentMonitor, ComponentStatus, StatusLevel
|
||||
from utils.lib import utc_now, error_embed
|
||||
from utils.ui import Confirm
|
||||
from utils.data import MULTIVALUE_IN, MEMBERS
|
||||
@@ -38,6 +39,10 @@ class ScheduleCog(LionCog):
|
||||
self.bot = bot
|
||||
self.data: ScheduleData = bot.db.load_registry(ScheduleData())
|
||||
self.settings = ScheduleSettings()
|
||||
self.monitor = ComponentMonitor(
|
||||
'ScheduleCog',
|
||||
self._monitor
|
||||
)
|
||||
|
||||
# Whether we are ready to take events
|
||||
self.initialised = asyncio.Event()
|
||||
@@ -57,12 +62,56 @@ class ScheduleCog(LionCog):
|
||||
|
||||
self.session_channels = self.settings.SessionChannels._cache
|
||||
|
||||
async def _monitor(self):
|
||||
nowid = self.nowid
|
||||
now = None
|
||||
now_lock = self.slotlock(nowid)
|
||||
if not self.initialised.is_set():
|
||||
level = StatusLevel.STARTING
|
||||
info = (
|
||||
"(STARTING) "
|
||||
"Not ready. "
|
||||
"Spawn task is {spawn}. "
|
||||
"Spawn lock is {spawn_lock}. "
|
||||
"Active slots {active}."
|
||||
)
|
||||
elif nowid not in self.active_slots:
|
||||
level = StatusLevel.UNSURE
|
||||
info = (
|
||||
"(UNSURE) "
|
||||
"Setup, but current slotid {nowid} not active. "
|
||||
"Spawn task is {spawn}. "
|
||||
"Spawn lock is {spawn_lock}. "
|
||||
"Now lock is {now_lock}. "
|
||||
"Active slots {active}."
|
||||
)
|
||||
else:
|
||||
now = self.active_slots[nowid]
|
||||
level = StatusLevel.OKAY
|
||||
info = (
|
||||
"(OK) "
|
||||
"Running current slot {now}. "
|
||||
"Spawn lock is {spawn_lock}. "
|
||||
"Now lock is {now_lock}. "
|
||||
"Active slots {active}."
|
||||
)
|
||||
data = {
|
||||
'spawn': self.spawn_task,
|
||||
'spawn_lock': self.spawn_lock,
|
||||
'active': self.active_slots,
|
||||
'nowid': nowid,
|
||||
'now_lock': now_lock,
|
||||
'now': now,
|
||||
}
|
||||
return ComponentStatus(level, info, info, data)
|
||||
|
||||
@property
|
||||
def nowid(self):
|
||||
now = utc_now()
|
||||
return time_to_slotid(now)
|
||||
|
||||
async def cog_load(self):
|
||||
self.bot.system_monitor.add_component(self.monitor)
|
||||
await self.data.init()
|
||||
|
||||
# Update the session channel cache
|
||||
|
||||
@@ -253,6 +253,12 @@ class ScheduledSession:
|
||||
overwrites = room.overwrites
|
||||
for member in members:
|
||||
mobj = guild.get_member(member.userid)
|
||||
if not mobj and not guild.chunked:
|
||||
self.bot.request_chunking_for(guild)
|
||||
try:
|
||||
mobj = await guild.fetch_member(member.userid)
|
||||
except discord.HTTPException:
|
||||
mobj = None
|
||||
if mobj:
|
||||
overwrites[mobj] = discord.PermissionOverwrite(connect=True, view_channel=True)
|
||||
try:
|
||||
@@ -297,6 +303,13 @@ class ScheduledSession:
|
||||
}
|
||||
for member in members:
|
||||
mobj = guild.get_member(member.userid)
|
||||
if not mobj and not guild.chunked:
|
||||
self.bot.request_chunking_for(guild)
|
||||
try:
|
||||
mobj = await guild.fetch_member(member.userid)
|
||||
except discord.HTTPException:
|
||||
mobj = None
|
||||
|
||||
if mobj:
|
||||
overwrites[mobj] = discord.PermissionOverwrite(connect=True, view_channel=True)
|
||||
try:
|
||||
|
||||
@@ -440,7 +440,7 @@ class TimeSlot:
|
||||
)
|
||||
|
||||
def launch(self) -> asyncio.Task:
|
||||
self.run_task = asyncio.create_task(self.run())
|
||||
self.run_task = asyncio.create_task(self.run(), name=f"TimeSlot {self.slotid}")
|
||||
return self.run_task
|
||||
|
||||
@log_wrap(action="TimeSlot Run")
|
||||
|
||||
@@ -117,6 +117,7 @@ class Shopping(LionCog):
|
||||
name=_p('cmd:shop', 'shop'),
|
||||
description=_p('cmd:shop|desc', "Purchase coloures, roles, and other goodies with LionCoins.")
|
||||
)
|
||||
@appcmds.guild_only
|
||||
async def shop_group(self, ctx: LionContext):
|
||||
return
|
||||
|
||||
@@ -124,6 +125,7 @@ class Shopping(LionCog):
|
||||
name=_p('cmd:shop_open', 'open'),
|
||||
description=_p('cmd:shop_open|desc', "Open the server shop.")
|
||||
)
|
||||
@appcmds.guild_only
|
||||
async def shop_open_cmd(self, ctx: LionContext):
|
||||
"""
|
||||
Opens the shop UI for the current guild.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import asyncio
|
||||
import logging
|
||||
from typing import Optional
|
||||
|
||||
@@ -90,8 +91,12 @@ class StatsCog(LionCog):
|
||||
)).format(loading=self.bot.config.emojis.loading),
|
||||
timestamp=utc_now(),
|
||||
)
|
||||
await ctx.interaction.response(embed=waiting_embed)
|
||||
await ctx.guild.chunk()
|
||||
await ctx.interaction.response.send_message(embed=waiting_embed)
|
||||
try:
|
||||
await asyncio.wait_for(ctx.guild.chunk(), timeout=10)
|
||||
pass
|
||||
except asyncio.TimeoutError:
|
||||
pass
|
||||
else:
|
||||
await ctx.interaction.response.defer(thinking=True)
|
||||
ui = LeaderboardUI(self.bot, ctx.author, ctx.guild)
|
||||
|
||||
@@ -44,7 +44,7 @@ async def get_profile_card(bot: LionBot, userid: int, guildid: int):
|
||||
if crank:
|
||||
roleid = crank.roleid
|
||||
role = guild.get_role(roleid)
|
||||
name = role.name if role else str(role.id)
|
||||
name = role.name if role else 'Unknown Rank'
|
||||
minimum = crank.required
|
||||
maximum = nrank.required if nrank else None
|
||||
rangestr = format_stat_range(rank_type, minimum, maximum)
|
||||
@@ -63,7 +63,7 @@ async def get_profile_card(bot: LionBot, userid: int, guildid: int):
|
||||
if nrank:
|
||||
roleid = nrank.roleid
|
||||
role = guild.get_role(roleid)
|
||||
name = role.name if role else str(role.id)
|
||||
name = role.name if role else 'Unknown Rank'
|
||||
minimum = nrank.required
|
||||
|
||||
guild_ranks = await ranks.get_guild_ranks(guildid)
|
||||
|
||||
@@ -75,6 +75,8 @@ class LeaderboardUI(StatsUI):
|
||||
# (type, period) -> (pagen -> Optional[Future[Card]])
|
||||
self.cache = {}
|
||||
|
||||
self.was_chunked: bool = guild.chunked
|
||||
|
||||
async def run(self, interaction: discord.Interaction):
|
||||
self._original = interaction
|
||||
|
||||
@@ -136,6 +138,7 @@ class LeaderboardUI(StatsUI):
|
||||
|
||||
# Filter out members which are not in the server and unranked roles and bots
|
||||
# Usually hits cache
|
||||
self.was_chunked = self.guild.chunked
|
||||
unranked_setting = await self.bot.get_cog('StatsCog').settings.UnrankedRoles.get(self.guild.id)
|
||||
unranked_roleids = set(unranked_setting.data)
|
||||
true_leaderboard = []
|
||||
@@ -299,42 +302,42 @@ class LeaderboardUI(StatsUI):
|
||||
|
||||
@button(label="This Season", style=ButtonStyle.grey)
|
||||
async def season_button(self, press: discord.Interaction, pressed: Button):
|
||||
await press.response.defer(thinking=True)
|
||||
await press.response.defer(thinking=True, ephemeral=True)
|
||||
self.current_period = LBPeriod.SEASON
|
||||
self.focused = True
|
||||
await self.refresh(thinking=press)
|
||||
|
||||
@button(label="Today", style=ButtonStyle.grey)
|
||||
async def day_button(self, press: discord.Interaction, pressed: Button):
|
||||
await press.response.defer(thinking=True)
|
||||
await press.response.defer(thinking=True, ephemeral=True)
|
||||
self.current_period = LBPeriod.DAY
|
||||
self.focused = True
|
||||
await self.refresh(thinking=press)
|
||||
|
||||
@button(label="This Week", style=ButtonStyle.grey)
|
||||
async def week_button(self, press: discord.Interaction, pressed: Button):
|
||||
await press.response.defer(thinking=True)
|
||||
await press.response.defer(thinking=True, ephemeral=True)
|
||||
self.current_period = LBPeriod.WEEK
|
||||
self.focused = True
|
||||
await self.refresh(thinking=press)
|
||||
|
||||
@button(label="This Month", style=ButtonStyle.grey)
|
||||
async def month_button(self, press: discord.Interaction, pressed: Button):
|
||||
await press.response.defer(thinking=True)
|
||||
await press.response.defer(thinking=True, ephemeral=True)
|
||||
self.current_period = LBPeriod.MONTH
|
||||
self.focused = True
|
||||
await self.refresh(thinking=press)
|
||||
|
||||
@button(label="All Time", style=ButtonStyle.grey)
|
||||
async def alltime_button(self, press: discord.Interaction, pressed: Button):
|
||||
await press.response.defer(thinking=True)
|
||||
await press.response.defer(thinking=True, ephemeral=True)
|
||||
self.current_period = LBPeriod.ALLTIME
|
||||
self.focused = True
|
||||
await self.refresh(thinking=press)
|
||||
|
||||
@button(emoji=conf.emojis.backward, style=ButtonStyle.grey)
|
||||
async def prev_button(self, press: discord.Interaction, pressed: Button):
|
||||
await press.response.defer(thinking=True)
|
||||
await press.response.defer(thinking=True, ephemeral=True)
|
||||
self.pagen -= 1
|
||||
self.focused = False
|
||||
await self.refresh(thinking=press)
|
||||
@@ -435,12 +438,19 @@ class LeaderboardUI(StatsUI):
|
||||
Generate UI message arguments from stored data
|
||||
"""
|
||||
t = self.bot.translator.t
|
||||
chunk_warning = t(_p(
|
||||
'ui:leaderboard|chunk_warning',
|
||||
"**Note:** Could not retrieve member list from Discord, so some members may be missing. "
|
||||
"Try again in a minute!"
|
||||
))
|
||||
if self.card is not None:
|
||||
period_start = self.period_starts[self.current_period]
|
||||
header = t(_p(
|
||||
'ui:leaderboard|since',
|
||||
"Counting statistics since {timestamp}"
|
||||
)).format(timestamp=discord.utils.format_dt(period_start))
|
||||
if not self.was_chunked:
|
||||
header = '\n'.join((header, chunk_warning))
|
||||
args = MessageArgs(
|
||||
embed=None,
|
||||
content=header,
|
||||
@@ -473,7 +483,11 @@ class LeaderboardUI(StatsUI):
|
||||
)),
|
||||
description=empty_description
|
||||
)
|
||||
args = MessageArgs(content=None, embed=embed, files=[])
|
||||
args = MessageArgs(
|
||||
content=chunk_warning if not self.was_chunked else None,
|
||||
embed=embed,
|
||||
files=[]
|
||||
)
|
||||
return args
|
||||
|
||||
async def refresh_components(self):
|
||||
|
||||
@@ -186,6 +186,17 @@ def mk_print(fp: io.StringIO) -> Callable[..., None]:
|
||||
return _print
|
||||
|
||||
|
||||
def mk_status_printer(bot, printer):
|
||||
async def _status(details=False):
|
||||
if details:
|
||||
status = await bot.system_monitor.get_overview()
|
||||
else:
|
||||
status = await bot.system_monitor.get_summary()
|
||||
printer(status)
|
||||
return status
|
||||
return _status
|
||||
|
||||
|
||||
@log_wrap(action="Code Exec")
|
||||
async def _async(to_eval: str, style='exec'):
|
||||
newline = '\n' * ('\n' in to_eval)
|
||||
@@ -202,6 +213,7 @@ async def _async(to_eval: str, style='exec'):
|
||||
scope['ctx'] = ctx = context.get()
|
||||
scope['bot'] = ctx_bot.get()
|
||||
scope['print'] = _print # type: ignore
|
||||
scope['print_status'] = mk_status_printer(scope['bot'], _print)
|
||||
|
||||
try:
|
||||
if ctx and ctx.message:
|
||||
@@ -297,7 +309,7 @@ class Exec(LionCog):
|
||||
file = discord.File(fp, filename=f"output-{target}.md")
|
||||
await ctx.reply(file=file)
|
||||
elif result:
|
||||
await ctx.reply(f"```md{result}```")
|
||||
await ctx.reply(f"```md\n{result}```")
|
||||
else:
|
||||
await ctx.reply("Command completed, and had no output.")
|
||||
else:
|
||||
@@ -351,7 +363,7 @@ class Exec(LionCog):
|
||||
except asyncio.TimeoutError:
|
||||
return
|
||||
if ctx.interaction:
|
||||
await ctx.interaction.response.defer(thinking=True, ephemeral=True)
|
||||
await ctx.interaction.response.defer(thinking=True)
|
||||
if target is not None:
|
||||
if target not in shard_talk.peers:
|
||||
embed = discord.Embed(description=f"Unknown peer {target}", colour=discord.Colour.red())
|
||||
@@ -376,7 +388,7 @@ class Exec(LionCog):
|
||||
await ctx.reply(file=file)
|
||||
else:
|
||||
# Send as message
|
||||
await ctx.reply(f"```md\n{output}```", ephemeral=True)
|
||||
await ctx.reply(f"```md\n{output}```")
|
||||
|
||||
asyncall_cmd.autocomplete('target')(_peer_acmpl)
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import asyncio
|
||||
import datetime
|
||||
|
||||
import discord
|
||||
@@ -22,8 +23,8 @@ class GuildLog(LionCog):
|
||||
embed.set_author(name="Left guild!")
|
||||
|
||||
# Add more specific information about the guild
|
||||
embed.add_field(name="Owner", value="{0.name} (ID: {0.id})".format(guild.owner), inline=False)
|
||||
embed.add_field(name="Members (cached)", value="{}".format(len(guild.members)), inline=False)
|
||||
embed.add_field(name="Owner", value="<@{}>".format(guild.owner_id), inline=False)
|
||||
embed.add_field(name="Members", value="{}".format(guild.member_count), inline=False)
|
||||
embed.add_field(name="Now studying in", value="{} guilds".format(len(self.bot.guilds)), inline=False)
|
||||
|
||||
# Retrieve the guild log channel and log the event
|
||||
@@ -35,8 +36,14 @@ class GuildLog(LionCog):
|
||||
@LionCog.listener('on_guild_join')
|
||||
@log_wrap(action="Log Guild Join")
|
||||
async def log_join_guild(self, guild: discord.Guild):
|
||||
owner = guild.owner
|
||||
try:
|
||||
await asyncio.wait_for(guild.chunk(), timeout=60)
|
||||
except asyncio.TimeoutError:
|
||||
pass
|
||||
|
||||
# TODO: Add info about when we last joined this guild etc once we have it.
|
||||
|
||||
if guild.chunked:
|
||||
bots = 0
|
||||
known = 0
|
||||
unknown = 0
|
||||
@@ -68,6 +75,12 @@ class GuildLog(LionCog):
|
||||
mem3,
|
||||
mem4
|
||||
)
|
||||
else:
|
||||
mem_str = (
|
||||
"`{count}` total members.\n"
|
||||
"(Could not chunk guild within `60` seconds.)"
|
||||
).format(count=guild.member_count)
|
||||
|
||||
created = "<t:{}>".format(int(guild.created_at.timestamp()))
|
||||
|
||||
embed = discord.Embed(
|
||||
@@ -77,7 +90,7 @@ class GuildLog(LionCog):
|
||||
)
|
||||
embed.set_author(name="Joined guild!")
|
||||
|
||||
embed.add_field(name="Owner", value="{0} (ID: {0.id})".format(owner), inline=False)
|
||||
embed.add_field(name="Owner", value="<@{}>".format(guild.owner_id), inline=False)
|
||||
embed.add_field(name="Created at", value=created, inline=False)
|
||||
embed.add_field(name="Members", value=mem_str, inline=False)
|
||||
embed.add_field(name="Now studying in", value="{} guilds".format(len(self.bot.guilds)), inline=False)
|
||||
|
||||
@@ -396,7 +396,7 @@ class RoleSetting(InteractiveSetting[ParentID, int, Union[discord.Role, discord.
|
||||
if data is not None:
|
||||
role = None
|
||||
|
||||
guildid = cls._get_guildid(parent_id)
|
||||
guildid = cls._get_guildid(parent_id, **kwargs)
|
||||
bot = ctx_bot.get()
|
||||
guild = bot.get_guild(guildid)
|
||||
if guild is not None:
|
||||
@@ -409,11 +409,14 @@ class RoleSetting(InteractiveSetting[ParentID, int, Union[discord.Role, discord.
|
||||
async def _parse_string(cls, parent_id, string: str, **kwargs):
|
||||
if not string or string.lower() == 'none':
|
||||
return None
|
||||
guildid = cls._get_guildid(parent_id, **kwargs)
|
||||
|
||||
t = ctx_translator.get().t
|
||||
bot = ctx_bot.get()
|
||||
role = None
|
||||
guild = bot.get_guild(parent_id)
|
||||
guild = bot.get_guild(guildid)
|
||||
if guild is None:
|
||||
raise ValueError("Attempting to parse role string with no guild.")
|
||||
|
||||
if string.isdigit():
|
||||
maybe_id = int(string)
|
||||
|
||||
@@ -80,6 +80,10 @@ class Bucket:
|
||||
self._last_full = False
|
||||
self._level += 1
|
||||
|
||||
def fill(self):
|
||||
self._leak()
|
||||
self._level = max(self._level, self.max_level + 1)
|
||||
|
||||
async def wait(self):
|
||||
"""
|
||||
Wait until the bucket has room.
|
||||
|
||||
@@ -10,6 +10,8 @@ from discord.ui import Modal, View, Item
|
||||
from meta.logger import log_action_stack, logging_context
|
||||
from meta.errors import SafeCancellation
|
||||
|
||||
from gui.errors import RenderingException
|
||||
|
||||
from . import logger
|
||||
from ..lib import MessageArgs, error_embed
|
||||
|
||||
@@ -228,6 +230,12 @@ class LeoUI(View):
|
||||
f"Caught a safe cancellation from LeoUI: {e.details}",
|
||||
extra={'action': 'Cancel'}
|
||||
)
|
||||
except RenderingException as e:
|
||||
logger.info(
|
||||
f"UI interaction failed due to rendering exception: {repr(e)}"
|
||||
)
|
||||
embed = interaction.client.tree.rendersplat(e)
|
||||
await interaction.client.tree.error_reply(interaction, embed)
|
||||
except Exception:
|
||||
logger.exception(
|
||||
f"Unhandled interaction exception occurred in item {item!r} of LeoUI {self!r} from interaction: "
|
||||
@@ -235,15 +243,8 @@ class LeoUI(View):
|
||||
extra={'with_ctx': True, 'action': 'UIError'}
|
||||
)
|
||||
# Explicitly handle the bugsplat ourselves
|
||||
if not interaction.is_expired():
|
||||
splat = interaction.client.tree.bugsplat(interaction, error)
|
||||
try:
|
||||
if interaction.response.is_done():
|
||||
await interaction.followup.send(embed=splat, ephemeral=True)
|
||||
else:
|
||||
await interaction.response.send_message(embed=splat, ephemeral=True)
|
||||
except discord.HTTPException:
|
||||
pass
|
||||
await interaction.client.tree.error_reply(interaction, splat)
|
||||
|
||||
|
||||
class MessageUI(LeoUI):
|
||||
@@ -475,21 +476,20 @@ class LeoModal(Modal):
|
||||
"""
|
||||
try:
|
||||
raise error
|
||||
except RenderingException as e:
|
||||
logger.info(
|
||||
f"Modal submit failed due to rendering exception: {repr(e)}"
|
||||
)
|
||||
embed = interaction.client.tree.rendersplat(e)
|
||||
await interaction.client.tree.error_reply(interaction, embed)
|
||||
except Exception:
|
||||
logger.exception(
|
||||
f"Unhandled interaction exception occurred in {self!r}. Interaction: {interaction.data}",
|
||||
extra={'with_ctx': True, 'action': 'ModalError'}
|
||||
)
|
||||
# Explicitly handle the bugsplat ourselves
|
||||
if not interaction.is_expired():
|
||||
splat = interaction.client.tree.bugsplat(interaction, error)
|
||||
try:
|
||||
if interaction.response.is_done():
|
||||
await interaction.followup.send(embed=splat, ephemeral=True)
|
||||
else:
|
||||
await interaction.response.send_message(embed=splat, ephemeral=True)
|
||||
except discord.HTTPException:
|
||||
pass
|
||||
await interaction.client.tree.error_reply(interaction, splat)
|
||||
|
||||
|
||||
def error_handler_for(exc):
|
||||
|
||||
22
src/wards.py
22
src/wards.py
@@ -21,14 +21,18 @@ async def sys_admin(bot: LionBot, userid: int):
|
||||
return userid in admins
|
||||
|
||||
|
||||
async def high_management(bot: LionBot, member: discord.Member):
|
||||
async def high_management(bot: LionBot, member: discord.Member, guild: discord.Guild):
|
||||
if not guild:
|
||||
return True
|
||||
if await sys_admin(bot, member.id):
|
||||
return True
|
||||
return member.guild_permissions.administrator
|
||||
|
||||
|
||||
async def low_management(bot: LionBot, member: discord.Member):
|
||||
if await high_management(bot, member):
|
||||
async def low_management(bot: LionBot, member: discord.Member, guild: discord.Guild):
|
||||
if not guild:
|
||||
return True
|
||||
if await high_management(bot, member, guild):
|
||||
return True
|
||||
return member.guild_permissions.manage_guild
|
||||
|
||||
@@ -42,20 +46,20 @@ async def sys_admin_iward(interaction: discord.Interaction) -> bool:
|
||||
async def high_management_iward(interaction: discord.Interaction) -> bool:
|
||||
if not interaction.guild:
|
||||
return False
|
||||
return await high_management(interaction.client, interaction.user)
|
||||
return await high_management(interaction.client, interaction.user, interaction.guild)
|
||||
|
||||
|
||||
async def low_management_iward(interaction: discord.Interaction) -> bool:
|
||||
if not interaction.guild:
|
||||
return False
|
||||
return await low_management(interaction.client, interaction.user)
|
||||
return await low_management(interaction.client, interaction.user, interaction.guild)
|
||||
|
||||
|
||||
# High level ctx wards
|
||||
async def moderator_ctxward(ctx: LionContext) -> bool:
|
||||
if not ctx.guild:
|
||||
return False
|
||||
passed = await low_management(ctx.bot, ctx.author)
|
||||
passed = await low_management(ctx.bot, ctx.author, ctx.guild)
|
||||
if passed:
|
||||
return True
|
||||
modrole = ctx.lguild.data.mod_role
|
||||
@@ -85,7 +89,7 @@ async def sys_admin_ward(ctx: LionContext) -> bool:
|
||||
async def high_management_ward(ctx: LionContext) -> bool:
|
||||
if not ctx.guild:
|
||||
return False
|
||||
passed = await high_management(ctx.bot, ctx.author)
|
||||
passed = await high_management(ctx.bot, ctx.author, ctx.guild)
|
||||
if passed:
|
||||
return True
|
||||
else:
|
||||
@@ -101,7 +105,7 @@ async def high_management_ward(ctx: LionContext) -> bool:
|
||||
async def low_management_ward(ctx: LionContext) -> bool:
|
||||
if not ctx.guild:
|
||||
return False
|
||||
passed = await low_management(ctx.bot, ctx.author)
|
||||
passed = await low_management(ctx.bot, ctx.author, ctx.guild)
|
||||
if passed:
|
||||
return True
|
||||
else:
|
||||
@@ -192,7 +196,7 @@ async def equippable_role(bot: LionBot, target_role: discord.Role, actor: discor
|
||||
"You need the `MANAGE_ROLES` permission before you can configure roles!"
|
||||
)).format(role=target_role.mention)
|
||||
)
|
||||
elif actor.top_role <= target_role and not actor == guild.owner:
|
||||
elif actor.top_role <= target_role and not actor.id == guild.owner_id:
|
||||
raise UserInputError(
|
||||
t(_p(
|
||||
'ward:equippable_role|error:actor_top_role',
|
||||
|
||||
Reference in New Issue
Block a user