diff --git a/locales/en_GB.po b/locales/en_GB.po
index e03b20bc0..b0c43938e 100644
--- a/locales/en_GB.po
+++ b/locales/en_GB.po
@@ -45,8 +45,10 @@ msgid "Please input a text"
msgstr "Please input some text"
#: lua/groupbutler/api_errors.lua:26
-msgid "This message is too long. Max lenght allowed by Telegram: 4000 characters"
-msgstr "This message is too long. Max length allowed by Telegram is 4000 characters"
+msgid ""
+"This message is too long. Max lenght allowed by Telegram: 4000 characters"
+msgstr ""
+"This message is too long. Max length allowed by Telegram is 4000 characters"
#: lua/groupbutler/api_errors.lua:27
msgid "One of the inline buttons you are trying to set is missing the URL"
@@ -54,7 +56,9 @@ msgstr ""
#: lua/groupbutler/api_errors.lua:28
#, lua-format
-msgid "Inline link formatted incorrectly. Check the text between brackets -> \\[]()\n"
+msgid ""
+"Inline link formatted incorrectly. Check the text between brackets -> \\[]"
+"()\n"
"%s"
msgstr ""
@@ -63,44 +67,63 @@ msgid "More info [here](https://telegram.me/GB_tutorials/12)"
msgstr ""
#: lua/groupbutler/api_errors.lua:29
-msgid "This text breaks the markdown.\n"
-"More info about a proper use of markdown [here](https://telegram.me/GB_tutorials/10) and [here](https://telegram.me/GB_tutorials/12)."
+msgid ""
+"This text breaks the markdown.\n"
+"More info about a proper use of markdown [here](https://telegram.me/"
+"GB_tutorials/10) and [here](https://telegram.me/GB_tutorials/12)."
msgstr ""
#: lua/groupbutler/api_errors.lua:31
-msgid "One of the URLs that should be placed in an inline button seems to be invalid (not an URL). Please check it"
+msgid ""
+"One of the URLs that should be placed in an inline button seems to be "
+"invalid (not an URL). Please check it"
msgstr ""
#: lua/groupbutler/api_errors.lua:32
msgid "One of the inline buttons you are trying to set doesn't have a name"
msgstr ""
-#: lua/groupbutler/main.lua:128
+#: lua/groupbutler/main.lua:151
#, lua-format
-msgid "Hello everyone!\n"
-"My name is %s, and I'm a bot made to help administrators in their hard work.\n"
-"Unfortunately I can't work in normal groups. If you need me, please ask the creator to convert this group to a supergroup and then add me again.\n"
+msgid ""
+"Hello everyone!\n"
+"My name is %s, and I'm a bot made to help administrators in their hard "
+"work.\n"
+"Unfortunately I can't work in normal groups. If you need me, please ask the "
+"creator to convert this group to a supergroup and then add me again.\n"
msgstr ""
-#: lua/groupbutler/main.lua:196
+#: lua/groupbutler/main.lua:219
msgid "🐞 Sorry, a *bug* occurred"
msgstr ""
+#: lua/groupbutler/message.lua:80
+msgid "Reply to a user or mention them"
+msgstr ""
+
+#: lua/groupbutler/message.lua:83
+msgid ""
+"I've never seen this user before.\n"
+"This command works by reply, username, user ID or text mention.\n"
+"If you're using it by username and want to teach me who the user is, forward "
+"me one of their messages"
+msgstr ""
+
#: lua/groupbutler/plugins/antispam.lua:43
#: lua/groupbutler/plugins/onmessage.lua:162
-#: lua/groupbutler/plugins/warn.lua:113
+#: lua/groupbutler/plugins/warn.lua:114
msgid "banned"
msgstr ""
#: lua/groupbutler/plugins/antispam.lua:44
#: lua/groupbutler/plugins/onmessage.lua:159
-#: lua/groupbutler/plugins/warn.lua:117
+#: lua/groupbutler/plugins/warn.lua:118
msgid "kicked"
msgstr ""
#: lua/groupbutler/plugins/antispam.lua:45
#: lua/groupbutler/plugins/onmessage.lua:165
-#: lua/groupbutler/plugins/warn.lua:121
+#: lua/groupbutler/plugins/warn.lua:122
msgid "muted"
msgstr ""
@@ -182,14 +205,15 @@ msgid "Allow/forbid forwarded messages from channels"
msgstr ""
#: lua/groupbutler/plugins/antispam.lua:204
-msgid "Set how many times the bot should warn users before kicking/banning them"
+msgid ""
+"Set how many times the bot should warn users before kicking/banning them"
msgstr ""
#: lua/groupbutler/plugins/antispam.lua:206
-#: lua/groupbutler/plugins/defaultpermissions.lua:66
-#: lua/groupbutler/plugins/floodmanager.lua:24
+#: lua/groupbutler/plugins/defaultpermissions.lua:68
+#: lua/groupbutler/plugins/floodmanager.lua:26
#: lua/groupbutler/plugins/logchannel.lua:37
-#: lua/groupbutler/plugins/menu.lua:42
+#: lua/groupbutler/plugins/menu.lua:44
#: lua/groupbutler/plugins/private_settings.lua:25
msgid "Description not available"
msgstr ""
@@ -207,20 +231,19 @@ msgid "Mute 👁"
msgstr ""
#: lua/groupbutler/plugins/antispam.lua:292
-#: lua/groupbutler/plugins/floodmanager.lua:144
#: lua/groupbutler/plugins/logchannel.lua:125
-#: lua/groupbutler/plugins/mediasettings.lua:115
-#: lua/groupbutler/plugins/menu.lua:258
msgid "You're no longer an admin"
msgstr ""
#: lua/groupbutler/plugins/antispam.lua:294
-msgid "*Anti-spam settings*\n"
+msgid ""
+"*Anti-spam settings*\n"
"Choose which kind of message you want to forbid\n"
"• ✅ = *Allowed*\n"
"• ❌ = *Not allowed*\n"
"• 🗑 = *Delete*\n"
-"When set on `delete`, the bot doesn't warn users until they are about to be kicked/banned/muted (at the second-to-last warning)"
+"When set on `delete`, the bot doesn't warn users until they are about to be "
+"kicked/banned/muted (at the second-to-last warning)"
msgstr ""
#: lua/groupbutler/plugins/antispam.lua:362
@@ -229,7 +252,8 @@ msgstr ""
#: lua/groupbutler/plugins/antispam.lua:365
#, lua-format
-msgid "*Whitelist cleaned*\n"
+msgid ""
+"*Whitelist cleaned*\n"
"%d links have been removed"
msgstr ""
@@ -248,17 +272,21 @@ msgstr ""
#: lua/groupbutler/plugins/antispam.lua:378
#: lua/groupbutler/plugins/antispam.lua:409
#, lua-format
-msgid "\n"
+msgid ""
+"\n"
"%d links were already in the list"
msgstr ""
#: lua/groupbutler/plugins/antispam.lua:390
-msgid "_The whitelist is empty_.\n"
+msgid ""
+"_The whitelist is empty_.\n"
"Use `/wl [links]` to add some links to the whitelist"
msgstr ""
#: lua/groupbutler/plugins/antispam.lua:392
-msgid "Whitelisted links:\n\n"
+msgid ""
+"Whitelisted links:\n"
+"\n"
msgstr ""
#: lua/groupbutler/plugins/antispam.lua:407
@@ -272,7 +300,8 @@ msgstr ""
#: lua/groupbutler/plugins/antispam.lua:426
#, lua-format
-msgid "*Whitelisted channels:*\n"
+msgid ""
+"*Whitelisted channels:*\n"
"%s"
msgstr ""
@@ -283,7 +312,9 @@ msgstr ""
#: lua/groupbutler/plugins/backup.lua:100
#, lua-format
-msgid "I'm sorry, this command has been used for the last time less then 3 hours ago by %s (ask them for the file).\n"
+msgid ""
+"I'm sorry, this command has been used for the last time less then 3 hours "
+"ago by %s (ask them for the file).\n"
"Wait [%s
] to use it again\n"
msgstr ""
@@ -292,7 +323,8 @@ msgid "*Sent in private*"
msgstr ""
#: lua/groupbutler/plugins/backup.lua:120
-msgid "Invalid input. Please reply to the backup file (/snap command to get it)"
+msgid ""
+"Invalid input. Please reply to the backup file (/snap command to get it)"
msgstr ""
#: lua/groupbutler/plugins/backup.lua:125
@@ -301,7 +333,8 @@ msgstr ""
#: lua/groupbutler/plugins/backup.lua:130
#, lua-format
-msgid "This is not a valid backup file.\n"
+msgid ""
+"This is not a valid backup file.\n"
"Reason: invalid name (%s)"
msgstr ""
@@ -316,94 +349,100 @@ msgid "Chat IDs don't match (%s and %s)"
msgstr ""
#: lua/groupbutler/plugins/backup.lua:172
-msgid "Import was successful.\n\n"
+msgid ""
+"Import was successful.\n"
+"\n"
"Important:\n"
-"- #extra commands which are associated with a media must be set again if the bot you are using now is different from the bot that originated the backup.\n"
+"- #extra commands which are associated with a media must be set again if the "
+"bot you are using now is different from the bot that originated the backup.\n"
msgstr ""
-#: lua/groupbutler/plugins/banhammer.lua:89
-msgid "Use -/+ to edit the value, then select a timeframe to temporary ban the user"
+#: lua/groupbutler/plugins/banhammer.lua:90
+msgid ""
+"Use -/+ to edit the value, then select a timeframe to temporary ban the user"
msgstr ""
-#: lua/groupbutler/plugins/banhammer.lua:96
+#: lua/groupbutler/plugins/banhammer.lua:106
#, lua-format
msgid "%s kicked %s!"
msgstr ""
-#: lua/groupbutler/plugins/banhammer.lua:105
-#: lua/groupbutler/plugins/banhammer.lua:118
+#: lua/groupbutler/plugins/banhammer.lua:120
+#: lua/groupbutler/plugins/banhammer.lua:139
#, lua-format
msgid "%s banned %s!"
msgstr ""
-#: lua/groupbutler/plugins/banhammer.lua:112
+#: lua/groupbutler/plugins/banhammer.lua:125
msgid "_Use this command in reply to a forwarded message_"
msgstr ""
-#: lua/groupbutler/plugins/banhammer.lua:126
+#: lua/groupbutler/plugins/banhammer.lua:144
msgid "_An admin can't be unbanned_"
msgstr ""
-#: lua/groupbutler/plugins/banhammer.lua:135
+#: lua/groupbutler/plugins/banhammer.lua:148
msgid "This user is not banned!"
msgstr ""
-#: lua/groupbutler/plugins/banhammer.lua:140
+#: lua/groupbutler/plugins/banhammer.lua:162
#, lua-format
msgid "%s unbanned by %s!"
msgstr ""
-#: lua/groupbutler/plugins/banhammer.lua:152
-#: lua/groupbutler/plugins/defaultpermissions.lua:127
+#: lua/groupbutler/plugins/banhammer.lua:174
+#: lua/groupbutler/plugins/defaultpermissions.lua:135
msgid "Sorry, you don't have permission to restrict members"
msgstr ""
-#: lua/groupbutler/plugins/banhammer.lua:156
-msgid "Tap on the -/+ buttons to change this value. Then select a timeframe to execute the ban"
+#: lua/groupbutler/plugins/banhammer.lua:180
+msgid ""
+"Tap on the -/+ buttons to change this value. Then select a timeframe to "
+"execute the ban"
msgstr ""
-#: lua/groupbutler/plugins/banhammer.lua:165
+#: lua/groupbutler/plugins/banhammer.lua:196
msgid "You can't set a lower value"
msgstr ""
-#: lua/groupbutler/plugins/banhammer.lua:173
+#: lua/groupbutler/plugins/banhammer.lua:204
msgid "Stop!!!"
msgstr ""
-#: lua/groupbutler/plugins/banhammer.lua:189
+#: lua/groupbutler/plugins/banhammer.lua:221
msgid "hours"
msgstr ""
-#: lua/groupbutler/plugins/banhammer.lua:193
+#: lua/groupbutler/plugins/banhammer.lua:225
msgid "days"
msgstr ""
-#: lua/groupbutler/plugins/banhammer.lua:197
+#: lua/groupbutler/plugins/banhammer.lua:229
msgid "minutes"
msgstr ""
-#: lua/groupbutler/plugins/banhammer.lua:203
+#: lua/groupbutler/plugins/banhammer.lua:237
#, lua-format
msgid "User banned for %d %s"
msgstr ""
-#: lua/groupbutler/plugins/configure.lua:19
+#: lua/groupbutler/plugins/configure.lua:20
msgid "🛠 Menu"
msgstr ""
-#: lua/groupbutler/plugins/configure.lua:20
+#: lua/groupbutler/plugins/configure.lua:21
msgid "⚡️ Antiflood"
msgstr ""
-#: lua/groupbutler/plugins/configure.lua:21
+#: lua/groupbutler/plugins/configure.lua:22
msgid "🌈 Media"
msgstr ""
-#: lua/groupbutler/plugins/configure.lua:22
+#: lua/groupbutler/plugins/configure.lua:23
msgid "🚫 Antispam"
msgstr ""
-#: lua/groupbutler/plugins/configure.lua:23
+#: lua/groupbutler/plugins/configure.lua:24
msgid "📥 Log channel"
msgstr ""
@@ -411,76 +450,75 @@ msgstr ""
msgid "⛔️ Default permissions"
msgstr ""
-#: lua/groupbutler/plugins/configure.lua:45
-#: lua/groupbutler/plugins/configure.lua:68
+#: lua/groupbutler/plugins/configure.lua:33
msgid "Change the settings of your group"
msgstr ""
-#: lua/groupbutler/plugins/configure.lua:58
+#: lua/groupbutler/plugins/configure.lua:66
msgid "_I've sent you the keyboard via private message_"
msgstr ""
#: lua/groupbutler/plugins/dashboard.lua:20
-#: lua/groupbutler/plugins/floodmanager.lua:36
+#: lua/groupbutler/plugins/floodmanager.lua:38
msgid "✅ | ON"
msgstr ""
#: lua/groupbutler/plugins/dashboard.lua:22
-#: lua/groupbutler/plugins/floodmanager.lua:38
+#: lua/groupbutler/plugins/floodmanager.lua:40
msgid "❌ | OFF"
msgstr ""
#: lua/groupbutler/plugins/dashboard.lua:29
-#: lua/groupbutler/plugins/menu.lua:160 lua/groupbutler/plugins/menu.lua:224
+#: lua/groupbutler/plugins/menu.lua:162 lua/groupbutler/plugins/menu.lua:226
msgid "👞 kick"
msgstr ""
#: lua/groupbutler/plugins/dashboard.lua:31
-#: lua/groupbutler/plugins/menu.lua:162
+#: lua/groupbutler/plugins/menu.lua:164
msgid "🔨 ban"
msgstr ""
#: lua/groupbutler/plugins/dashboard.lua:33
-#: lua/groupbutler/plugins/floodmanager.lua:49
-#: lua/groupbutler/plugins/menu.lua:164 lua/groupbutler/plugins/menu.lua:228
+#: lua/groupbutler/plugins/floodmanager.lua:51
+#: lua/groupbutler/plugins/menu.lua:166 lua/groupbutler/plugins/menu.lua:230
msgid "👁 mute"
msgstr ""
#: lua/groupbutler/plugins/dashboard.lua:38
-#: lua/groupbutler/plugins/floodmanager.lua:67
+#: lua/groupbutler/plugins/floodmanager.lua:69
msgid "Texts"
msgstr ""
#: lua/groupbutler/plugins/dashboard.lua:39
-#: lua/groupbutler/plugins/floodmanager.lua:68
+#: lua/groupbutler/plugins/floodmanager.lua:70
msgid "Forwards"
msgstr ""
#: lua/groupbutler/plugins/dashboard.lua:40
#: lua/groupbutler/plugins/dashboard.lua:167
-#: lua/groupbutler/plugins/floodmanager.lua:69
-#: lua/groupbutler/plugins/mediasettings.lua:42
+#: lua/groupbutler/plugins/floodmanager.lua:71
+#: lua/groupbutler/plugins/mediasettings.lua:44
msgid "Stickers"
msgstr ""
#: lua/groupbutler/plugins/dashboard.lua:41
#: lua/groupbutler/plugins/dashboard.lua:159
-#: lua/groupbutler/plugins/floodmanager.lua:70
-#: lua/groupbutler/plugins/mediasettings.lua:33
+#: lua/groupbutler/plugins/floodmanager.lua:72
+#: lua/groupbutler/plugins/mediasettings.lua:35
msgid "Images"
msgstr ""
#: lua/groupbutler/plugins/dashboard.lua:42
#: lua/groupbutler/plugins/dashboard.lua:160
-#: lua/groupbutler/plugins/floodmanager.lua:71
-#: lua/groupbutler/plugins/mediasettings.lua:34
+#: lua/groupbutler/plugins/floodmanager.lua:73
+#: lua/groupbutler/plugins/mediasettings.lua:36
msgid "GIFs"
msgstr ""
#: lua/groupbutler/plugins/dashboard.lua:43
#: lua/groupbutler/plugins/dashboard.lua:161
-#: lua/groupbutler/plugins/floodmanager.lua:72
-#: lua/groupbutler/plugins/mediasettings.lua:35
+#: lua/groupbutler/plugins/floodmanager.lua:74
+#: lua/groupbutler/plugins/mediasettings.lua:37
msgid "Videos"
msgstr ""
@@ -501,7 +539,8 @@ msgstr ""
#: lua/groupbutler/plugins/dashboard.lua:60
#, lua-format
-msgid "- *Ignored media*:\n"
+msgid ""
+"- *Ignored media*:\n"
"%s"
msgstr ""
@@ -514,7 +553,7 @@ msgid "Admins"
msgstr ""
#: lua/groupbutler/plugins/dashboard.lua:71
-#: lua/groupbutler/plugins/menu.lua:181 lua/groupbutler/utilities.lua:592
+#: lua/groupbutler/plugins/menu.lua:183 lua/groupbutler/utilities.lua:474
msgid "Rules"
msgstr ""
@@ -544,7 +583,9 @@ msgid "🚫 This group does not exist"
msgstr ""
#: lua/groupbutler/plugins/dashboard.lua:126
-msgid "🚷 You are not a member of the chat. You can't see the settings of a private group."
+msgid ""
+"🚷 You are not a member of the chat. You can't see the settings of a private "
+"group."
msgstr ""
#: lua/groupbutler/plugins/dashboard.lua:133
@@ -572,7 +613,7 @@ msgid "ℹ️ Group ► Flood"
msgstr ""
#: lua/groupbutler/plugins/dashboard.lua:162
-#: lua/groupbutler/plugins/mediasettings.lua:37
+#: lua/groupbutler/plugins/mediasettings.lua:39
msgid "Documents"
msgstr ""
@@ -581,85 +622,100 @@ msgid "Voice Messages"
msgstr ""
#: lua/groupbutler/plugins/dashboard.lua:165
-#: lua/groupbutler/plugins/mediasettings.lua:40
+#: lua/groupbutler/plugins/mediasettings.lua:42
msgid "Links"
msgstr ""
#: lua/groupbutler/plugins/dashboard.lua:166
-#: lua/groupbutler/plugins/mediasettings.lua:41
+#: lua/groupbutler/plugins/mediasettings.lua:43
msgid "Music"
msgstr ""
#: lua/groupbutler/plugins/dashboard.lua:168
-#: lua/groupbutler/plugins/mediasettings.lua:43
+#: lua/groupbutler/plugins/mediasettings.lua:45
msgid "Contacts"
msgstr ""
#: lua/groupbutler/plugins/dashboard.lua:169
-#: lua/groupbutler/plugins/mediasettings.lua:44
+#: lua/groupbutler/plugins/mediasettings.lua:46
msgid "Games"
msgstr ""
#: lua/groupbutler/plugins/dashboard.lua:170
-#: lua/groupbutler/plugins/mediasettings.lua:45
+#: lua/groupbutler/plugins/mediasettings.lua:47
msgid "Locations"
msgstr ""
#: lua/groupbutler/plugins/dashboard.lua:171
-#: lua/groupbutler/plugins/mediasettings.lua:46
+#: lua/groupbutler/plugins/mediasettings.lua:48
msgid "Venues"
msgstr ""
#: lua/groupbutler/plugins/dashboard.lua:173
-msgid "*Current media settings*:\n\n"
+msgid ""
+"*Current media settings*:\n"
+"\n"
msgstr ""
#: lua/groupbutler/plugins/dashboard.lua:185
msgid "ℹ️ Group ► Media"
msgstr ""
-#: lua/groupbutler/plugins/defaultpermissions.lua:62
-msgid "Permission to send messages. If disabled, the user won't be able to send any kind of message"
+#: lua/groupbutler/plugins/defaultpermissions.lua:64
+msgid ""
+"Permission to send messages. If disabled, the user won't be able to send any "
+"kind of message"
msgstr ""
-#: lua/groupbutler/plugins/defaultpermissions.lua:63
-msgid "Permission to send media (audios, documents, photos, videos, video notes and voice notes). Implies the permission to send messages"
+#: lua/groupbutler/plugins/defaultpermissions.lua:65
+msgid ""
+"Permission to send media (audios, documents, photos, videos, video notes and "
+"voice notes). Implies the permission to send messages"
msgstr ""
-#: lua/groupbutler/plugins/defaultpermissions.lua:64
-msgid "Permission to send other types of messages (GIFs, games, stickers and use inline bots). Implies the permission to send medias"
+#: lua/groupbutler/plugins/defaultpermissions.lua:66
+msgid ""
+"Permission to send other types of messages (GIFs, games, stickers and use "
+"inline bots). Implies the permission to send medias"
msgstr ""
-#: lua/groupbutler/plugins/defaultpermissions.lua:65
-msgid "When disabled, user's messages with a link won't show the web page preview"
+#: lua/groupbutler/plugins/defaultpermissions.lua:67
+msgid ""
+"When disabled, user's messages with a link won't show the web page preview"
msgstr ""
-#: lua/groupbutler/plugins/defaultpermissions.lua:74
+#: lua/groupbutler/plugins/defaultpermissions.lua:76
msgid "Send messages"
msgstr ""
-#: lua/groupbutler/plugins/defaultpermissions.lua:75
+#: lua/groupbutler/plugins/defaultpermissions.lua:77
msgid "Send media"
msgstr ""
-#: lua/groupbutler/plugins/defaultpermissions.lua:76
+#: lua/groupbutler/plugins/defaultpermissions.lua:78
msgid "Send other types of media"
msgstr ""
-#: lua/groupbutler/plugins/defaultpermissions.lua:77
+#: lua/groupbutler/plugins/defaultpermissions.lua:79
msgid "Show web page preview"
msgstr ""
-#: lua/groupbutler/plugins/defaultpermissions.lua:129
-msgid "*Default permissions*\n"
-"From this menu you can change the default permissions that will be granted when a new member join.\n"
-"_Only the administrators with the permission to restrict a member can access this menu._\n"
-"Tap on the name of a permission for a description of what kind of messages it will influence.\n"
+#: lua/groupbutler/plugins/defaultpermissions.lua:139
+msgid ""
+"*Default permissions*\n"
+"From this menu you can change the default permissions that will be granted "
+"when a new member join.\n"
+"_Only the administrators with the permission to restrict a member can access "
+"this menu._\n"
+"Tap on the name of a permission for a description of what kind of messages "
+"it will influence.\n"
msgstr ""
-#: lua/groupbutler/plugins/defaultpermissions.lua:151
+#: lua/groupbutler/plugins/defaultpermissions.lua:161
#, lua-format
-msgid "Setting saved, but I can't edit the buttons because you are too fast! Wait other %d seconds"
+msgid ""
+"Setting saved, but I can't edit the buttons because you are too fast! Wait "
+"other %d seconds"
msgstr ""
#: lua/groupbutler/plugins/extra.lua:81
@@ -679,7 +735,8 @@ msgstr ""
#: lua/groupbutler/plugins/extra.lua:119
#, lua-format
-msgid "\n"
+msgid ""
+"\n"
"Commands not found: `%s`"
msgstr ""
@@ -688,57 +745,68 @@ msgstr ""
msgid "_Please_ [start me](%s) _so I can send you the answer_"
msgstr ""
-#: lua/groupbutler/plugins/floodmanager.lua:18
+#: lua/groupbutler/plugins/floodmanager.lua:20
msgid "⚖ Current sensitivity. Tap on the + or the - to change it"
msgstr ""
-#: lua/groupbutler/plugins/floodmanager.lua:20
-msgid "Choose which media must be ignored by the antiflood (the bot won't consider them).\n"
+#: lua/groupbutler/plugins/floodmanager.lua:22
+msgid ""
+"Choose which media must be ignored by the antiflood (the bot won't consider "
+"them).\n"
"✅: ignored\n"
"❌: not ignored"
msgstr ""
-#: lua/groupbutler/plugins/floodmanager.lua:45
+#: lua/groupbutler/plugins/floodmanager.lua:47
msgid "👞️ kick"
msgstr ""
-#: lua/groupbutler/plugins/floodmanager.lua:47
+#: lua/groupbutler/plugins/floodmanager.lua:49
msgid "🔨 ️ban"
msgstr ""
-#: lua/groupbutler/plugins/floodmanager.lua:106
+#: lua/groupbutler/plugins/floodmanager.lua:108
msgid "Flooders will be banned"
msgstr ""
-#: lua/groupbutler/plugins/floodmanager.lua:109
+#: lua/groupbutler/plugins/floodmanager.lua:111
msgid "Flooders will be kicked"
msgstr ""
-#: lua/groupbutler/plugins/floodmanager.lua:112
+#: lua/groupbutler/plugins/floodmanager.lua:114
msgid "Flooders will be muted"
msgstr ""
-#: lua/groupbutler/plugins/floodmanager.lua:121
-#: lua/groupbutler/plugins/floodmanager.lua:128
+#: lua/groupbutler/plugins/floodmanager.lua:123
+#: lua/groupbutler/plugins/floodmanager.lua:130
#, lua-format
msgid "%d is not a valid value!\n"
msgstr ""
-#: lua/groupbutler/plugins/floodmanager.lua:146
-msgid "You can manage the antiflood settings from here.\n\n"
-"It is also possible to choose which type of messages the antiflood will ignore (✅)"
+#: lua/groupbutler/plugins/floodmanager.lua:157
+#: lua/groupbutler/plugins/mediasettings.lua:127
+#: lua/groupbutler/plugins/menu.lua:271
+msgid "Sorry, you don't have permission to change settings"
msgstr ""
-#: lua/groupbutler/plugins/floodmanager.lua:153
-msgid "Antiflood settings"
+#: lua/groupbutler/plugins/floodmanager.lua:161
+msgid ""
+"You can manage the antiflood settings from here.\n"
+"\n"
+"It is also possible to choose which type of messages the antiflood will "
+"ignore (✅)"
msgstr ""
#: lua/groupbutler/plugins/floodmanager.lua:168
+msgid "Antiflood settings"
+msgstr ""
+
+#: lua/groupbutler/plugins/floodmanager.lua:177
#, lua-format
msgid "❎ [%s] will be ignored by the anti-flood"
msgstr ""
-#: lua/groupbutler/plugins/floodmanager.lua:171
+#: lua/groupbutler/plugins/floodmanager.lua:180
#, lua-format
msgid "🚫 [%s] won't be ignored by the anti-flood"
msgstr ""
@@ -749,33 +817,54 @@ msgstr ""
#: lua/groupbutler/plugins/help.lua:23
#, lua-format
-msgid "Hello %s 👋🏼, nice to meet you!\n"
-"I'm Group Butler, the first administration bot using the official Bot API.\n\n"
+msgid ""
+"Hello %s 👋🏼, nice to meet you!\n"
+"I'm Group Butler, the first administration bot using the official Bot API.\n"
+"\n"
"*I can do a lot of cool stuffs*, here's a short list:\n"
"• I can *kick or ban* users\n"
"• You can use me to set the group rules\n"
"• I have a flexible *anti-flood* system\n"
-"• I can *welcome new users* with a customizable message, or if you want with a gif or a sticker\n"
-"• I can *warn* users, and ban them when they reach the maximum number of warnings\n"
+"• I can *welcome new users* with a customizable message, or if you want with "
+"a gif or a sticker\n"
+"• I can *warn* users, and ban them when they reach the maximum number of "
+"warnings\n"
"• I can also warn, kick or ban users when they post a specific media\n"
-"…and more, below you can find the \"all commands\" button to get the whole list!\n\n"
-"I work better if you add me to the group administrators (otherwise I won't be able to kick or ban)!"
+"…and more, below you can find the \"all commands\" button to get the whole "
+"list!\n"
+"\n"
+"I work better if you add me to the group administrators (otherwise I won't "
+"be able to kick or ban)!"
msgstr ""
#: lua/groupbutler/plugins/help.lua:36
-msgid "This bot works only in supergroups.\n\n"
-"To work properly, [it needs to be admin in your group](https://telegram.me/GroupButler_ch/104), so it can kick or ban people if needed.\n"
-"Only the group owner can promote it :)\n\n"
-"You can use `/, ! or #` to trigger a command.\n\n"
-"Group Butler saves the adminlist of a group in its databse to avoid to send too many requests to Telegram.\n"
-"This list is updated every 5 hours, so there could be some differences between who the bot thinks are the admins and who the admins actually are, if during the 5 hours timeframe some users have been promoted/demoted.\n"
-"It's possible to force the bot to update its adminlist with `/cache`.\n\n"
-"Remember: you have to use commands *in the group*, unless they are specifically designed for private chats (see \"private\" tab)."
+msgid ""
+"This bot works only in supergroups.\n"
+"\n"
+"To work properly, [it needs to be admin in your group](https://telegram.me/"
+"GroupButler_ch/104), so it can kick or ban people if needed.\n"
+"Only the group owner can promote it :)\n"
+"\n"
+"You can use `/, ! or #` to trigger a command.\n"
+"\n"
+"Group Butler saves the adminlist of a group in its databse to avoid to send "
+"too many requests to Telegram.\n"
+"This list is updated every 5 hours, so there could be some differences "
+"between who the bot thinks are the admins and who the admins actually are, "
+"if during the 5 hours timeframe some users have been promoted/demoted.\n"
+"It's possible to force the bot to update its adminlist with `/cache`.\n"
+"\n"
+"Remember: you have to use commands *in the group*, unless they are "
+"specifically designed for private chats (see \"private\" tab)."
msgstr ""
#: lua/groupbutler/plugins/help.lua:48
-msgid "*Commands that work in private*:\n\n"
-"• `/mysettings`: show a keyboard that allows you to change your personal settings, such as choosing if receive the rules in private when you join a group or if receive reports made with the `@admin` command\n"
+msgid ""
+"*Commands that work in private*:\n"
+"\n"
+"• `/mysettings`: show a keyboard that allows you to change your personal "
+"settings, such as choosing if receive the rules in private when you join a "
+"group or if receive reports made with the `@admin` command\n"
"• `/echo [text]` : the bot will send the text back, formatted with markdown\n"
"• `/about` : show some useful informations about the bot\n"
"• `/groups` : show the list of the discussion groups\n"
@@ -785,175 +874,315 @@ msgid "*Commands that work in private*:\n\n"
msgstr ""
#: lua/groupbutler/plugins/help.lua:57
-msgid "*Commands available for every user in a group*:\n\n"
+msgid ""
+"*Commands available for every user in a group*:\n"
+"\n"
"• `/dashboard`: see all the informations about the group\n"
"• `/rules`: show the group rules\n"
"• `/adminlist`: show the administrators of the group\n"
"• `/help`: receive the help message\n"
"• `!kickme`: the bot will kick you\n"
-"*Note*: `/dashboard`, `/adminlist` and `/staff` always reply in private. If the bot is unable to reach a user, it will ask in the group to that user to be started, but just if _silent mode_ is off.\n"
-"With `/rules`, the bot always answer in the group for admins, but with normal users the message is sent in the group or in private according to the group settings.\n\n"
-"• `@admin` (by reply): report a message to the admins of the group (the bot will forward it in prvate). This ability could be turned off from the group settings. A description of the report can be added.\n"
-"Admins need to give their consense to receive reports from users, with `/mysettings` command"
+"*Note*: `/dashboard`, `/adminlist` and `/staff` always reply in private. If "
+"the bot is unable to reach a user, it will ask in the group to that user to "
+"be started, but just if _silent mode_ is off.\n"
+"With `/rules`, the bot always answer in the group for admins, but with "
+"normal users the message is sent in the group or in private according to the "
+"group settings.\n"
+"\n"
+"• `@admin` (by reply): report a message to the admins of the group (the bot "
+"will forward it in prvate). This ability could be turned off from the group "
+"settings. A description of the report can be added.\n"
+"Admins need to give their consense to receive reports from users, with `/"
+"mysettings` command"
msgstr ""
#: lua/groupbutler/plugins/help.lua:69
-msgid "*Admins: info about the group*\n\n"
-"• `/setrules [group rules]`: set the new regulation for the group (the old will be overwritten).\n"
-"• `/setrules -`: delete the current rules.\n\n"
-"*Note*: the markdown is supported. If the text sent breaks the markdown, the bot will notify that something is wrong.\n"
-"For a correct use of the markdown, check [this post](https://telegram.me/GroupButler_ch/46) in the channel\n\n"
-"• `/setlink [link|-]`: set the group link, so it can be re-called by other admins, or unset it.\n"
-"If you are going to use it in a public supergroup, you do not need to append the group link. Just send `/setlink`\n"
+msgid ""
+"*Admins: info about the group*\n"
+"\n"
+"• `/setrules [group rules]`: set the new regulation for the group (the old "
+"will be overwritten).\n"
+"• `/setrules -`: delete the current rules.\n"
+"\n"
+"*Note*: the markdown is supported. If the text sent breaks the markdown, the "
+"bot will notify that something is wrong.\n"
+"For a correct use of the markdown, check [this post](https://telegram.me/"
+"GroupButler_ch/46) in the channel\n"
+"\n"
+"• `/setlink [link|-]`: set the group link, so it can be re-called by other "
+"admins, or unset it.\n"
+"If you are going to use it in a public supergroup, you do not need to append "
+"the group link. Just send `/setlink`\n"
"• `/link`: get the group link, if already set.\n"
-"• `/msglink`: get the link to a message. Works only in public supergroups\n\n"
-"*Note*: the bot can recognize valid group links. If a link is not valid, you won't receive a reply."
+"• `/msglink`: get the link to a message. Works only in public supergroups\n"
+"\n"
+"*Note*: the bot can recognize valid group links. If a link is not valid, you "
+"won't receive a reply."
msgstr ""
#: lua/groupbutler/plugins/help.lua:83
-msgid "*Banhammer powers*\n"
-"A set of commands that let admins kick and ban people from a group, and get some information about a user.\n"
-"Kicked people can join back, banned people can't. Banned users are added to the group's blacklist. It's possible to blacklist users even if they are not part of the group.\n"
-"Only the administrators who have the permission to restrict users can use these commands, but `/status` can be used by all the admins.\n\n"
+msgid ""
+"*Banhammer powers*\n"
+"A set of commands that let admins kick and ban people from a group, and get "
+"some information about a user.\n"
+"Kicked people can join back, banned people can't. Banned users are added to "
+"the group's blacklist. It's possible to blacklist users even if they are not "
+"part of the group.\n"
+"Only the administrators who have the permission to restrict users can use "
+"these commands, but `/status` can be used by all the admins.\n"
+"\n"
"• `/kick [by reply|username|id|text mention]`: kick a user from the group.\n"
"• `/ban [by reply|username|id|text mention]`: ban a user from the group.\n"
-"• `/tempban [by reply|username|id|text mention]`: ban a user for a specific amount of time. Use the returned keyboard to ban the user.\n"
-"Pass a value on a new line to use it as starting value. When a ban expires, the user won't be added back. Check the Telegram's restricted users list for pending unbans.\n"
+"• `/tempban [by reply|username|id|text mention]`: ban a user for a specific "
+"amount of time. Use the returned keyboard to ban the user.\n"
+"Pass a value on a new line to use it as starting value. When a ban expires, "
+"the user won't be added back. Check the Telegram's restricted users list for "
+"pending unbans.\n"
"• `/fwdban [by reply]`: ban the original sender of a forwarded message.\n"
-"• `/unban [by reply|username|id|text mention]`: unban the user from the group.\n"
-"• `/user [by reply|username|id|text mention]`: shows how many times the user has been banned *in all the groups*, and the warns received.\n"
-"• `/status [username|id]`: show the current status of the user `(member|restricted|kicked/left the chat|banned|admin/creator|never seen)`.\n"
-"Will also show the permissions the user *doesn't* have.\n\n"
+"• `/unban [by reply|username|id|text mention]`: unban the user from the "
+"group.\n"
+"• `/user [by reply|username|id|text mention]`: shows how many times the user "
+"has been banned *in all the groups*, and the warns received.\n"
+"• `/status [username|id]`: show the current status of the user `(member|"
+"restricted|kicked/left the chat|banned|admin/creator|never seen)`.\n"
+"Will also show the permissions the user *doesn't* have.\n"
+"\n"
"*Antiflood*\n"
-"The \"antiflood\" is a system that auto-removes people that send many consecutive messages in a group.\n"
-"If on, the antiflood system will kick/ban flooders.\n\n"
-"• `/config` command, then `antiflood` button: manage the flood settings in private, with an inline keyboard. You can change the sensitivity, the action (kick/ban) to perform, and even set some exceptions."
+"The \"antiflood\" is a system that auto-removes people that send many "
+"consecutive messages in a group.\n"
+"If on, the antiflood system will kick/ban flooders.\n"
+"\n"
+"• `/config` command, then `antiflood` button: manage the flood settings in "
+"private, with an inline keyboard. You can change the sensitivity, the action "
+"(kick/ban) to perform, and even set some exceptions."
msgstr ""
#: lua/groupbutler/plugins/help.lua:103
-msgid "*Reports settings*\n"
-"`@admin` is an useful command to let users report some messages to the group admins.\n"
-"A reported message will be forwarded to the available admins.\n\n"
-"• `/config` command, then `menu` button: here you can find an option, \"Report\". If turned on, users will be able to use the `@admin` command.\n"
-"Only admins who accepted to receive reports (with `/mysettings` command) will be notified\n"
-"• `/mysettings` (in private): from here, you can choose if receive reports or not\n\n"
-"*Note*: admins can't use the `@admin` command, and users can't report admins with it."
+msgid ""
+"*Reports settings*\n"
+"`@admin` is an useful command to let users report some messages to the group "
+"admins.\n"
+"A reported message will be forwarded to the available admins.\n"
+"\n"
+"• `/config` command, then `menu` button: here you can find an option, "
+"\"Report\". If turned on, users will be able to use the `@admin` command.\n"
+"Only admins who accepted to receive reports (with `/mysettings` command) "
+"will be notified\n"
+"• `/mysettings` (in private): from here, you can choose if receive reports "
+"or not\n"
+"\n"
+"*Note*: admins can't use the `@admin` command, and users can't report admins "
+"with it."
msgstr ""
#: lua/groupbutler/plugins/help.lua:112
-msgid "*Welcome/goodbye settings*\n\n"
-"• `/config`, then `menu` tab: receive in private the menu keyboard. You will find an option to enable/disable welcome/goodbye messages.\n"
-"*Note*: goodbye messages don't work in large groups. This is a Telegram limitation that can't be avoided.\n\n"
+msgid ""
+"*Welcome/goodbye settings*\n"
+"\n"
+"• `/config`, then `menu` tab: receive in private the menu keyboard. You will "
+"find an option to enable/disable welcome/goodbye messages.\n"
+"*Note*: goodbye messages don't work in large groups. This is a Telegram "
+"limitation that can't be avoided.\n"
+"\n"
"*Custom welcome message*:\n"
"• `/welcome Welcome $name, enjoy the group!`\n"
-"Write after `/welcome` your welcome message. `/goodbye` works in the same way.\n\n"
-"You can use some placeholders to include the name/username/id of the new member of the group\n"
+"Write after `/welcome` your welcome message. `/goodbye` works in the same "
+"way.\n"
+"\n"
+"You can use some placeholders to include the name/username/id of the new "
+"member of the group\n"
"Placeholders:\n"
"`$username`: _will be replaced with the username_\n"
"`$name`: _will be replaced with the name_\n"
"`$id`: _will be replaced with the id_\n"
"`$title`: _will be replaced with the group title_\n"
"`$surname`: _will be replaced by the user's last name_\n"
-"`$rules`: _will be replaced by a link to the rules of the group. Please read_ [here](https://telegram.me/GroupButler_beta/26) _how to use it, or you will get an error for sure_\n"
-"*Note*: `$name`, `$surname`, and `$title` may not work properly within markdown markup.\n\n"
+"`$rules`: _will be replaced by a link to the rules of the group. Please "
+"read_ [here](https://telegram.me/GroupButler_beta/26) _how to use it, or you "
+"will get an error for sure_\n"
+"*Note*: `$name`, `$surname`, and `$title` may not work properly within "
+"markdown markup.\n"
+"\n"
"*GIF/sticker as welcome message*\n"
-"You can use a particular gif/sticker as welcome message. To set it, reply to the gif/sticker you want to set as welcome message with `/welcome`. Same goes for `/goodbye`"
+"You can use a particular gif/sticker as welcome message. To set it, reply to "
+"the gif/sticker you want to set as welcome message with `/welcome`. Same "
+"goes for `/goodbye`"
msgstr ""
#: lua/groupbutler/plugins/help.lua:133
-msgid "*Whitelist settings*\n\n"
-"As you may know, the bot can warn/kick/ban who sends a telegram.me link (antispam settings) or any other link (media settings).\n"
+msgid ""
+"*Whitelist settings*\n"
+"\n"
+"As you may know, the bot can warn/kick/ban who sends a telegram.me link "
+"(antispam settings) or any other link (media settings).\n"
"The whitelist is a list of links that will be ignored by the bot.\n"
-"If users send a whitelisted link, they won't be warned or kicked.\n\n"
-"`/whitelist [link(s)]` or `/wl [link(s)]`: add one or more links to the whitelist.\n"
-"`/unwhitelist [link(s)]` or `/unwl [link(s)]`: remove one or more links from the whitelist.\n"
+"If users send a whitelisted link, they won't be warned or kicked.\n"
+"\n"
+"`/whitelist [link(s)]` or `/wl [link(s)]`: add one or more links to the "
+"whitelist.\n"
+"`/unwhitelist [link(s)]` or `/unwl [link(s)]`: remove one or more links from "
+"the whitelist.\n"
"`/whitelist` or `/wl`: get the whitelist.\n"
-"`/whitelistl -` or `/wl -`: empty the whitelist.\n\n"
-"When the group link is saved with `/setlink`, it gets automatically added to the whitelist.\n\n"
+"`/whitelistl -` or `/wl -`: empty the whitelist.\n"
+"\n"
+"When the group link is saved with `/setlink`, it gets automatically added to "
+"the whitelist.\n"
+"\n"
"*Why links are saved without* _https://_ *and* _www_*?*\n"
-"The bot auto-removes _https://, http:// and www_ from every link to reduce the possibility of having the same link saved twice."
+"The bot auto-removes _https://, http:// and www_ from every link to reduce "
+"the possibility of having the same link saved twice."
msgstr ""
#: lua/groupbutler/plugins/help.lua:148
-msgid "*Extra commands*\n"
-"#extra commands are a smart way to save your own custom commands.\n\n"
-"• `/extra [#trigger] [reply]`: set a reply to be sent when someone writes the trigger.\n"
-"_Example_ : with \"`/extra #hello Good morning!`\", the bot will reply \"Good morning!\" each time someone writes #hello.\n"
-"You can reply to a media (_photo, file, vocal, video, gif, audio_) with `/extra #yourtrigger` to save the #extra and receive that media each time you use # command\n"
+msgid ""
+"*Extra commands*\n"
+"#extra commands are a smart way to save your own custom commands.\n"
+"\n"
+"• `/extra [#trigger] [reply]`: set a reply to be sent when someone writes "
+"the trigger.\n"
+"_Example_ : with \"`/extra #hello Good morning!`\", the bot will reply "
+"\"Good morning!\" each time someone writes #hello.\n"
+"You can reply to a media (_photo, file, vocal, video, gif, audio_) with `/"
+"extra #yourtrigger` to save the #extra and receive that media each time you "
+"use # command\n"
"• `/extra list`: get the list of your custom commands.\n"
-"• `/extra del [#trigger]`: delete the trigger and its message.\n\n"
-"*Note:* the markdown is supported. If the text sent breaks the markdown, the bot will notify that something is wrong.\n"
-"For a correct use of the markdown, check [this post](https://telegram.me/GroupButler_ch/46) in the channel.\n"
-"Now supports placeholders. Check the \"welcome\" tab for the list of the available placeholders"
+"• `/extra del [#trigger]`: delete the trigger and its message.\n"
+"\n"
+"*Note:* the markdown is supported. If the text sent breaks the markdown, the "
+"bot will notify that something is wrong.\n"
+"For a correct use of the markdown, check [this post](https://telegram.me/"
+"GroupButler_ch/46) in the channel.\n"
+"Now supports placeholders. Check the \"welcome\" tab for the list of the "
+"available placeholders"
msgstr ""
#: lua/groupbutler/plugins/help.lua:160
-msgid "*Warns*\n"
-"Warn are made to keep the count of the admonitions received by a user. Once users have been warned for the defined number of times, they are kicked/banned by the bot.\n"
+msgid ""
+"*Warns*\n"
+"Warn are made to keep the count of the admonitions received by a user. Once "
+"users have been warned for the defined number of times, they are kicked/"
+"banned by the bot.\n"
"There are two different type of warns:\n"
"- _normal warns_, given by an admin with the `/warn` command\n"
-"- _automatic warns_ (read: media warns and spam warns), given by the bot when someone sends a media that is not allowed in the chat, or spams other channels or telegram.me links.\n\n"
+"- _automatic warns_ (read: media warns and spam warns), given by the bot "
+"when someone sends a media that is not allowed in the chat, or spams other "
+"channels or telegram.me links.\n"
+"\n"
"• `/warn [by reply]`: warn a user\n"
-"• `/sw`: you can place a `/sw` (_\"silent warn\"_) everywhere you want in your message. The bot will silently count the warn, but won't answer in the group unless the user reached the max. number of warnings.\n"
-"• `/nowarns [by reply]`: reset the warns received by a user (both normal and automatic warns).\n"
+"• `/sw`: you can place a `/sw` (_\"silent warn\"_) everywhere you want in "
+"your message. The bot will silently count the warn, but won't answer in the "
+"group unless the user reached the max. number of warnings.\n"
+"• `/nowarns [by reply]`: reset the warns received by a user (both normal and "
+"automatic warns).\n"
"• `/warnmax [number]`: set the max number of the warns before the kick/ban.\n"
-"• `/warnmax media [number]`: set the max number of the warns before kick/ban when an unallowed media is sent.\n\n"
-"How to see how many warns a user has received (or to reset them): `/user` command.\n"
-"How to change the max. number of warnings allowed: `/config` command, then `menu` button.\n"
-"How to change the max. number of warnings allowed for medias: `/config` command, then `media` button.\n"
-"How to change the max. number of warnings allowed for spam: `/config` command, then `antispam` button."
+"• `/warnmax media [number]`: set the max number of the warns before kick/ban "
+"when an unallowed media is sent.\n"
+"\n"
+"How to see how many warns a user has received (or to reset them): `/user` "
+"command.\n"
+"How to change the max. number of warnings allowed: `/config` command, then "
+"`menu` button.\n"
+"How to change the max. number of warnings allowed for medias: `/config` "
+"command, then `media` button.\n"
+"How to change the max. number of warnings allowed for spam: `/config` "
+"command, then `antispam` button."
msgstr ""
#: lua/groupbutler/plugins/help.lua:176
-msgid "*Pinning messages*\n"
+msgid ""
+"*Pinning messages*\n"
"The \"48 hours limit\" to edit your own messages doesn't apply to bots.\n"
-"This command was born from the necessity of editing the pinned message without sending it again, maybe just to change few things.\n"
-"So with `/pin` you can generate a message to pin, and edit it how many times you want.\n\n"
-"• `/pin [text]`: the bot will send you back the text you used as argument, with markdown. You can pin the message and use `/pin [text]` again to edit it\n"
-"• `/pin`: the bot will find the latest message generate by `/pin`, if it still exists\n"
-"• `/newpin [text]`: forces the bot to send another message that will be saved as new target for `/pin`\n\n"
+"This command was born from the necessity of editing the pinned message "
+"without sending it again, maybe just to change few things.\n"
+"So with `/pin` you can generate a message to pin, and edit it how many times "
+"you want.\n"
+"\n"
+"• `/pin [text]`: the bot will send you back the text you used as argument, "
+"with markdown. You can pin the message and use `/pin [text]` again to edit "
+"it\n"
+"• `/pin`: the bot will find the latest message generate by `/pin`, if it "
+"still exists\n"
+"• `/newpin [text]`: forces the bot to send another message that will be "
+"saved as new target for `/pin`\n"
+"\n"
"*Note*: `/pin` supports markdown, but only `$rules` and `$title` placeholders"
msgstr ""
#: lua/groupbutler/plugins/help.lua:186
-msgid "*Group language*\n"
-"• `/lang`: change the bot language (works on groups and private chats)\n\n"
-"*Note*: the translators are volunteers, so neither the correctness nor completeness of localizations can be guaranteed.\n\n"
-"You can help improve translations on our [Crowdin Project](https://crowdin.com/project/group-butler).\n\n"
-"*Special characters*\n\n"
-"• `/config` command, then `menu` button: you will receive in private the menu keyboard.\n"
-"Here you will find two particular options: _Arab and RTL_.\n\n"
-"*Arab*: when Arab is not allowed (🚫), people who write Arab characters will be kicked from the group.\n"
-"*Rtl*: stands for 'Right To Left' character, is the cause of weird service messages written in the opposite direction.\n"
-"When Rtl is not allowed (🚫), people who write Rtl characters (or have it in their names) will be kicked."
+msgid ""
+"*Group language*\n"
+"• `/lang`: change the bot language (works on groups and private chats)\n"
+"\n"
+"*Note*: the translators are volunteers, so neither the correctness nor "
+"completeness of localizations can be guaranteed.\n"
+"\n"
+"You can help improve translations on our [Crowdin Project](https://crowdin."
+"com/project/group-butler).\n"
+"\n"
+"*Special characters*\n"
+"\n"
+"• `/config` command, then `menu` button: you will receive in private the "
+"menu keyboard.\n"
+"Here you will find two particular options: _Arab and RTL_.\n"
+"\n"
+"*Arab*: when Arab is not allowed (🚫), people who write Arab characters will "
+"be kicked from the group.\n"
+"*Rtl*: stands for 'Right To Left' character, is the cause of weird service "
+"messages written in the opposite direction.\n"
+"When Rtl is not allowed (🚫), people who write Rtl characters (or have it in "
+"their names) will be kicked."
msgstr ""
#: lua/groupbutler/plugins/help.lua:201
-msgid "*General group settings*\n\n"
-"`/config` or `/settings`: manage the group settings in private from an inline keyboard.\n"
-"The inline keyboard has six sub-menus:\n\n"
+msgid ""
+"*General group settings*\n"
+"\n"
+"`/config` or `/settings`: manage the group settings in private from an "
+"inline keyboard.\n"
+"The inline keyboard has six sub-menus:\n"
+"\n"
"*Menu*: manage the most important group settings\n"
-"*Antiflood*: turn on or off the antiflood, set its sensitivity and choose some media to ignore, if you want\n"
-"*Media*: choose which media to forbid in your group, and set the number of times that a user will be warned before being kicked/banned\n"
-"*Antispam*: choose which kind of message you want to forbid (e.g. telegram.me links, forwarded messages from channels)\n"
-"*Log channel*: choose which updates should be logged\n\n"
+"*Antiflood*: turn on or off the antiflood, set its sensitivity and choose "
+"some media to ignore, if you want\n"
+"*Media*: choose which media to forbid in your group, and set the number of "
+"times that a user will be warned before being kicked/banned\n"
+"*Antispam*: choose which kind of message you want to forbid (e.g. telegram."
+"me links, forwarded messages from channels)\n"
+"*Log channel*: choose which updates should be logged\n"
+"\n"
"*Bonus commands*:\n"
-"`/reportflood [number of messages]/[timeframe]`: set how many times users can use the @admin command within a certain timeframe.\n"
-"`/leave`: the bot will leave the group without deleting its data. Use this command only if you are going to add the bot to the group again\n"
-"`/snap`: generate a backup file that can be restored with `/import` (send the file in the group and reply to it). `/snap` can be used once every three days"
+"`/reportflood [number of messages]/[timeframe]`: set how many times users "
+"can use the @admin command within a certain timeframe.\n"
+"`/leave`: the bot will leave the group without deleting its data. Use this "
+"command only if you are going to add the bot to the group again\n"
+"`/snap`: generate a backup file that can be restored with `/import` (send "
+"the file in the group and reply to it). `/snap` can be used once every three "
+"days"
msgstr ""
#: lua/groupbutler/plugins/help.lua:216
-msgid "*Log channel informations*\n\n"
-"A log channel is a _(private)_ channel where the bot will record all the important events that will happen in your group.\n"
-"If you want to use this feature, you need to pair your group with a channel with the commands described below.\n"
-"All the events, by default, are *not logged*. Admins can choose which events to log from the `/config` menu -> `log channel` button.\n\n"
-"To pair a channel with a group, the *channel creator* must [add the bot to the channel administrators](telegram.me/gb_tutorials/4) (otherwise it won't be able to post), and send in the channel this command:\n"
+msgid ""
+"*Log channel informations*\n"
+"\n"
+"A log channel is a _(private)_ channel where the bot will record all the "
+"important events that will happen in your group.\n"
+"If you want to use this feature, you need to pair your group with a channel "
+"with the commands described below.\n"
+"All the events, by default, are *not logged*. Admins can choose which events "
+"to log from the `/config` menu -> `log channel` button.\n"
+"\n"
+"To pair a channel with a group, the *channel creator* must [add the bot to "
+"the channel administrators](telegram.me/gb_tutorials/4) (otherwise it won't "
+"be able to post), and send in the channel this command:\n"
"`/setlog`\n"
-"Then, an admin of the group must forward in the group the message (\"`/setlog`\") sent in the channel. *Done*!\n"
-"(you can find a video-tutorial [here](https://telegram.me/GB_tutorials/8))\n\n"
+"Then, an admin of the group must forward in the group the message (\"`/"
+"setlog`\") sent in the channel. *Done*!\n"
+"(you can find a video-tutorial [here](https://telegram.me/GB_tutorials/8))\n"
+"\n"
"A channel can be used as log by different groups.\n"
-"To change your log channel, simply repeat this process with another channel.\n\n"
+"To change your log channel, simply repeat this process with another "
+"channel.\n"
+"\n"
"`/unsetlog`: remove your current log channel\n"
"`/logchannel`: get some informations about your log channel, if paired"
msgstr ""
@@ -1069,7 +1298,9 @@ msgid "❗️ Already there"
msgstr ""
#: lua/groupbutler/plugins/links.lua:34
-msgid "*No link* for this group. Ask the owner to save it with `/setlink [group link]`"
+msgid ""
+"*No link* for this group. Ask the owner to save it with `/setlink [group "
+"link]`"
msgstr ""
#: lua/groupbutler/plugins/links.lua:45
@@ -1078,12 +1309,15 @@ msgstr ""
#: lua/groupbutler/plugins/links.lua:51 lua/groupbutler/plugins/links.lua:68
#, lua-format
-msgid "The link has been set.\n"
+msgid ""
+"The link has been set.\n"
"*Here's the link*: %s"
msgstr ""
#: lua/groupbutler/plugins/links.lua:54
-msgid "This is not a *public supergroup*, so you need to write the link near `/setlink`"
+msgid ""
+"This is not a *public supergroup*, so you need to write the link near `/"
+"setlink`"
msgstr ""
#: lua/groupbutler/plugins/links.lua:57
@@ -1092,7 +1326,8 @@ msgstr ""
#: lua/groupbutler/plugins/links.lua:66
#, lua-format
-msgid "The link has been updated.\n"
+msgid ""
+"The link has been updated.\n"
"*Here's the new link*: %s"
msgstr ""
@@ -1117,7 +1352,9 @@ msgid "Forbidden media will be logged in the channel"
msgstr ""
#: lua/groupbutler/plugins/logchannel.lua:28
-msgid "Spam links/forwards from channels will be logged in the channel, only if forbidden"
+msgid ""
+"Spam links/forwards from channels will be logged in the channel, only if "
+"forbidden"
msgstr ""
#: lua/groupbutler/plugins/logchannel.lua:29
@@ -1160,7 +1397,7 @@ msgstr ""
msgid "Kick"
msgstr ""
-#: lua/groupbutler/plugins/logchannel.lua:61 lua/groupbutler/utilities.lua:957
+#: lua/groupbutler/plugins/logchannel.lua:61 lua/groupbutler/utilities.lua:776
msgid "Unban"
msgstr ""
@@ -1229,9 +1466,11 @@ msgid "User unbanned!"
msgstr ""
#: lua/groupbutler/plugins/logchannel.lua:136
-msgid "*Select the events the will be logged in the channel*\n"
+msgid ""
+"*Select the events the will be logged in the channel*\n"
"✅ = will be logged\n"
-"☑️ = won't be logged\n\n"
+"☑️ = won't be logged\n"
+"\n"
"Tap on an option to get further information"
msgstr ""
@@ -1283,246 +1522,270 @@ msgid "*Log channel removed*"
msgstr ""
#: lua/groupbutler/plugins/logchannel.lua:216
-msgid "_This group has a log channel saved, but I'm not a member there, so I can't post/retrieve its info_"
+msgid ""
+"_This group has a log channel saved, but I'm not a member there, so I can't "
+"post/retrieve its info_"
msgstr ""
#: lua/groupbutler/plugins/logchannel.lua:223
#, lua-format
-msgid "This group has a log channel\n"
+msgid ""
+"This group has a log channel\n"
"Channel: %s
"
msgstr ""
-#: lua/groupbutler/plugins/mediasettings.lua:36
+#: lua/groupbutler/plugins/mediasettings.lua:38
msgid "Video messages"
msgstr ""
-#: lua/groupbutler/plugins/mediasettings.lua:39
+#: lua/groupbutler/plugins/mediasettings.lua:41
msgid "Vocal messages"
msgstr ""
-#: lua/groupbutler/plugins/mediasettings.lua:65
+#: lua/groupbutler/plugins/mediasettings.lua:67
#, lua-format
msgid "Warnings | %d | kick"
msgstr ""
-#: lua/groupbutler/plugins/mediasettings.lua:67
+#: lua/groupbutler/plugins/mediasettings.lua:69
#, lua-format
msgid "Warnings | %d | mute"
msgstr ""
-#: lua/groupbutler/plugins/mediasettings.lua:69
+#: lua/groupbutler/plugins/mediasettings.lua:71
#, lua-format
msgid "Warnings | %d | ban"
msgstr ""
-#: lua/groupbutler/plugins/mediasettings.lua:94
+#: lua/groupbutler/plugins/mediasettings.lua:96
msgid "❌ warning"
msgstr ""
-#: lua/groupbutler/plugins/mediasettings.lua:97
+#: lua/groupbutler/plugins/mediasettings.lua:99
msgid "🗑 delete"
msgstr ""
-#: lua/groupbutler/plugins/mediasettings.lua:103
+#: lua/groupbutler/plugins/mediasettings.lua:105
msgid "✅ allowed"
msgstr ""
-#: lua/groupbutler/plugins/mediasettings.lua:117
-msgid "\n"
-"Tap on an option on the right to *change the setting*\n"
-"You can use the last lines to change how many warnings the bot should give before kicking/banning/muting someone.\n"
-"The number is not related the the normal `/warn` command.\n\n"
-"Possible statuses: ✅ allowed, ❌ warning, 🗑 delete.\n"
-"When a media is set to delete, the bot will give a warning *only* when this is the users last warning\n"
+#: lua/groupbutler/plugins/mediasettings.lua:116
+msgid "⚠️ Tap on the right column"
msgstr ""
#: lua/groupbutler/plugins/mediasettings.lua:131
-msgid "⚠️ Tap on the right column"
+msgid ""
+"Tap on an option on the right to *change the setting*\n"
+"You can use the last lines to change how many warnings the bot should give "
+"before kicking/banning/muting someone.\n"
+"The number is not related the the normal `/warn` command.\n"
+"\n"
+"Possible statuses: ✅ allowed, ❌ warning, 🗑 delete.\n"
+"When a media is set to delete, the bot will give a warning *only* when this "
+"is the users last warning"
msgstr ""
-#: lua/groupbutler/plugins/mediasettings.lua:140
+#: lua/groupbutler/plugins/mediasettings.lua:149
msgid "⚙ The new value is too low ( < 1)"
msgstr ""
-#: lua/groupbutler/plugins/mediasettings.lua:147
+#: lua/groupbutler/plugins/mediasettings.lua:156
msgid "⚙ The new value is too high ( > 12)"
msgstr ""
-#: lua/groupbutler/plugins/mediasettings.lua:161
+#: lua/groupbutler/plugins/mediasettings.lua:171
msgid "👞 New status is kick"
msgstr ""
-#: lua/groupbutler/plugins/mediasettings.lua:164
+#: lua/groupbutler/plugins/mediasettings.lua:174
msgid "👁 New status is mute"
msgstr ""
-#: lua/groupbutler/plugins/mediasettings.lua:167
+#: lua/groupbutler/plugins/mediasettings.lua:177
msgid "🔨 New status is ban"
msgstr ""
#. TRANSLATORS: these strings should be shorter than 200 characters
-#: lua/groupbutler/plugins/menu.lua:24
-msgid "When enabled, users will be able to report messages with the @admin command"
-msgstr ""
-
-#: lua/groupbutler/plugins/menu.lua:25
-msgid "Enable or disable the goodbye message. Can't be sent in large groups"
-msgstr ""
-
#: lua/groupbutler/plugins/menu.lua:26
-msgid "Enable or disable the welcome message"
+msgid ""
+"When enabled, users will be able to report messages with the @admin command"
msgstr ""
#: lua/groupbutler/plugins/menu.lua:27
-msgid "When enabled, every time a new welcome message is sent, the previously sent welcome message is removed"
+msgid "Enable or disable the goodbye message. Can't be sent in large groups"
msgstr ""
#: lua/groupbutler/plugins/menu.lua:28
-msgid "When enabled, the bot doesn't answer in the group to /dashboard, /config and /help commands (it will just answer in private)"
+msgid "Enable or disable the welcome message"
msgstr ""
#: lua/groupbutler/plugins/menu.lua:29
-msgid "Enable and disable the anti-flood system (more info in the /help message)"
+msgid ""
+"When enabled, every time a new welcome message is sent, the previously sent "
+"welcome message is removed"
msgstr ""
#: lua/groupbutler/plugins/menu.lua:30
-msgid "If the welcome message is enabled, it will include an inline button that will send to the user the rules in private"
+msgid ""
+"When enabled, the bot doesn't answer in the group to /dashboard, /config "
+"and /help commands (it will just answer in private)"
msgstr ""
#: lua/groupbutler/plugins/menu.lua:31
-msgid "When someone uses /rules\n"
+msgid ""
+"Enable and disable the anti-flood system (more info in the /help message)"
+msgstr ""
+
+#: lua/groupbutler/plugins/menu.lua:32
+msgid ""
+"If the welcome message is enabled, it will include an inline button that "
+"will send to the user the rules in private"
+msgstr ""
+
+#: lua/groupbutler/plugins/menu.lua:33
+msgid ""
+"When someone uses /rules\n"
"👥: the bot will answer in the group (always, with admins)\n"
"👤: the bot will answer in private"
msgstr ""
-#: lua/groupbutler/plugins/menu.lua:34
-msgid "When someone uses an #extra\n"
+#: lua/groupbutler/plugins/menu.lua:36
+msgid ""
+"When someone uses an #extra\n"
"👥: the bot will answer in the group (always, with admins)\n"
"👤: the bot will answer in private"
msgstr ""
-#: lua/groupbutler/plugins/menu.lua:37
-msgid "Select what the bot should do when someone sends a message with arab characters"
+#: lua/groupbutler/plugins/menu.lua:39
+msgid ""
+"Select what the bot should do when someone sends a message with arab "
+"characters"
msgstr ""
-#: lua/groupbutler/plugins/menu.lua:38
+#: lua/groupbutler/plugins/menu.lua:40
msgid "Bots will be banned when added by normal users"
msgstr ""
-#: lua/groupbutler/plugins/menu.lua:39
-msgid "Select what the bot should do when someone sends a message with the RTL character, or has it in their name"
+#: lua/groupbutler/plugins/menu.lua:41
+msgid ""
+"Select what the bot should do when someone sends a message with the RTL "
+"character, or has it in their name"
msgstr ""
-#: lua/groupbutler/plugins/menu.lua:40
-msgid "Change how many times a user has to be warned before being kicked/banned"
+#: lua/groupbutler/plugins/menu.lua:42
+msgid ""
+"Change how many times a user has to be warned before being kicked/banned"
msgstr ""
-#: lua/groupbutler/plugins/menu.lua:41
-msgid "Change the action to perform when a user reaches the max. number of warnings"
+#: lua/groupbutler/plugins/menu.lua:43
+msgid ""
+"Change the action to perform when a user reaches the max. number of warnings"
msgstr ""
-#: lua/groupbutler/plugins/menu.lua:55
+#: lua/groupbutler/plugins/menu.lua:57
msgid "The new value is too high ( > 12)"
msgstr ""
-#: lua/groupbutler/plugins/menu.lua:62
+#: lua/groupbutler/plugins/menu.lua:64
msgid "The new value is too low ( < 1)"
msgstr ""
-#: lua/groupbutler/plugins/menu.lua:73
+#: lua/groupbutler/plugins/menu.lua:75
msgid "New action on max number of warns received: ban"
msgstr ""
-#: lua/groupbutler/plugins/menu.lua:76
+#: lua/groupbutler/plugins/menu.lua:78
msgid "New action on max number of warns received: mute"
msgstr ""
-#: lua/groupbutler/plugins/menu.lua:79
+#: lua/groupbutler/plugins/menu.lua:81
msgid "New action on max number of warns received: kick"
msgstr ""
-#: lua/groupbutler/plugins/menu.lua:88
+#: lua/groupbutler/plugins/menu.lua:90
msgid "Action -> kick"
msgstr ""
-#: lua/groupbutler/plugins/menu.lua:89
+#: lua/groupbutler/plugins/menu.lua:91
msgid "Action -> ban"
msgstr ""
-#: lua/groupbutler/plugins/menu.lua:90
+#: lua/groupbutler/plugins/menu.lua:92
msgid "Action -> mute"
msgstr ""
-#: lua/groupbutler/plugins/menu.lua:91
+#: lua/groupbutler/plugins/menu.lua:93
msgid "Allowed ✅"
msgstr ""
-#: lua/groupbutler/plugins/menu.lua:166
+#: lua/groupbutler/plugins/menu.lua:168
msgid "✅"
msgstr ""
-#: lua/groupbutler/plugins/menu.lua:176
+#: lua/groupbutler/plugins/menu.lua:178
msgid "Welcome"
msgstr ""
-#: lua/groupbutler/plugins/menu.lua:177
+#: lua/groupbutler/plugins/menu.lua:179
msgid "Goodbye"
msgstr ""
-#: lua/groupbutler/plugins/menu.lua:178 lua/groupbutler/utilities.lua:588
+#: lua/groupbutler/plugins/menu.lua:180 lua/groupbutler/utilities.lua:470
msgid "Extra"
msgstr ""
-#: lua/groupbutler/plugins/menu.lua:179 lua/groupbutler/utilities.lua:589
+#: lua/groupbutler/plugins/menu.lua:181 lua/groupbutler/utilities.lua:471
msgid "Anti-flood"
msgstr ""
-#: lua/groupbutler/plugins/menu.lua:180 lua/groupbutler/utilities.lua:591
+#: lua/groupbutler/plugins/menu.lua:182 lua/groupbutler/utilities.lua:473
msgid "Silent mode"
msgstr ""
-#: lua/groupbutler/plugins/menu.lua:182 lua/groupbutler/utilities.lua:593
+#: lua/groupbutler/plugins/menu.lua:184 lua/groupbutler/utilities.lua:475
msgid "Arab"
msgstr ""
-#: lua/groupbutler/plugins/menu.lua:183 lua/groupbutler/utilities.lua:594
+#: lua/groupbutler/plugins/menu.lua:185 lua/groupbutler/utilities.lua:476
msgid "RTL"
msgstr ""
-#: lua/groupbutler/plugins/menu.lua:184 lua/groupbutler/utilities.lua:590
+#: lua/groupbutler/plugins/menu.lua:186 lua/groupbutler/utilities.lua:472
msgid "Ban bots"
msgstr ""
-#: lua/groupbutler/plugins/menu.lua:185 lua/groupbutler/utilities.lua:595
+#: lua/groupbutler/plugins/menu.lua:187 lua/groupbutler/utilities.lua:477
msgid "Reports"
msgstr ""
-#: lua/groupbutler/plugins/menu.lua:186 lua/groupbutler/utilities.lua:596
+#: lua/groupbutler/plugins/menu.lua:188 lua/groupbutler/utilities.lua:478
msgid "Delete last welcome message"
msgstr ""
-#: lua/groupbutler/plugins/menu.lua:187
+#: lua/groupbutler/plugins/menu.lua:189
msgid "Welcome + rules button"
msgstr ""
-#: lua/groupbutler/plugins/menu.lua:188 lua/groupbutler/utilities.lua:598
+#: lua/groupbutler/plugins/menu.lua:190 lua/groupbutler/utilities.lua:480
msgid "Clean Service Messages"
msgstr ""
-#: lua/groupbutler/plugins/menu.lua:226
+#: lua/groupbutler/plugins/menu.lua:228
msgid "🔨️ ban"
msgstr ""
-#: lua/groupbutler/plugins/menu.lua:232
+#: lua/groupbutler/plugins/menu.lua:234
msgid "Warns: "
msgstr ""
-#: lua/groupbutler/plugins/menu.lua:237
+#: lua/groupbutler/plugins/menu.lua:239
msgid "Action:"
msgstr ""
-#: lua/groupbutler/plugins/menu.lua:260
-msgid "Manage the settings of the group. Click on the left column to get a small hint"
+#: lua/groupbutler/plugins/menu.lua:275
+msgid ""
+"Manage the settings of the group. Click on the left column to get a small "
+"hint"
msgstr ""
#: lua/groupbutler/plugins/onmessage.lua:117
@@ -1542,19 +1805,22 @@ msgstr ""
#: lua/groupbutler/plugins/onmessage.lua:170
#, lua-format
-msgid "%s %s: media sent not allowed!\n"
+msgid ""
+"%s %s: media sent not allowed!\n"
"❗️ %d/%d
"
msgstr ""
#: lua/groupbutler/plugins/onmessage.lua:180
#, lua-format
-msgid "%s, this type of media is not allowed in this chat.\n"
+msgid ""
+"%s, this type of media is not allowed in this chat.\n"
"(%d/%d
)"
msgstr ""
#: lua/groupbutler/plugins/onmessage.lua:185
#, lua-format
-msgid "%s, this type of media is not allowed in this chat.\n"
+msgid ""
+"%s, this type of media is not allowed in this chat.\n"
"The next time you will be banned/kicked/muted\n"
msgstr ""
@@ -1597,7 +1863,8 @@ msgid "Last message generated by /pin
^"
msgstr ""
#: lua/groupbutler/plugins/pin.lua:86
-msgid "The old message generated with /pin
does not exist anymore."
+msgid ""
+"The old message generated with /pin
does not exist anymore."
msgstr ""
#: lua/groupbutler/plugins/private.lua:17
@@ -1606,9 +1873,13 @@ msgstr ""
#: lua/groupbutler/plugins/private.lua:25
#, lua-format
-msgid "This bot is based on [otouto](https://github.com/topkecleon/otouto) (AKA @mokubot, channel: @otouto), a multipurpose Lua bot.\n"
-"Group Butler wouldn't exist without it.\n\n"
-"You can contact the owners of this bot using the /groups command.\n\n"
+msgid ""
+"This bot is based on [otouto](https://github.com/topkecleon/otouto) (AKA "
+"@mokubot, channel: @otouto), a multipurpose Lua bot.\n"
+"Group Butler wouldn't exist without it.\n"
+"\n"
+"You can contact the owners of this bot using the /groups command.\n"
+"\n"
"Bot version: %s\n"
"*Some useful links:*"
msgstr ""
@@ -1644,11 +1915,15 @@ msgid "🔙 back"
msgstr ""
#: lua/groupbutler/plugins/private_settings.lua:23
-msgid "When you join a group moderated by this bot, you will receive the group rules in private"
+msgid ""
+"When you join a group moderated by this bot, you will receive the group "
+"rules in private"
msgstr ""
#: lua/groupbutler/plugins/private_settings.lua:24
-msgid "If enabled, you will receive all the messages reported with the @admin command in the groups you are moderating"
+msgid ""
+"If enabled, you will receive all the messages reported with the @admin "
+"command in the groups you are moderating"
msgstr ""
#: lua/groupbutler/plugins/private_settings.lua:35
@@ -1678,31 +1953,36 @@ msgstr ""
#: lua/groupbutler/plugins/report.lua:33
#, lua-format
-msgid "\n"
+msgid ""
+"\n"
"• Reported message sent by: %s (%d
)"
msgstr ""
#: lua/groupbutler/plugins/report.lua:37
#, lua-format
-msgid "\n"
+msgid ""
+"\n"
"• Group: %s"
msgstr ""
#: lua/groupbutler/plugins/report.lua:39
#, lua-format
-msgid "\n"
+msgid ""
+"\n"
"• Group: %s"
msgstr ""
#: lua/groupbutler/plugins/report.lua:43
#, lua-format
-msgid "\n"
+msgid ""
+"\n"
"• Go to the message"
msgstr ""
#: lua/groupbutler/plugins/report.lua:47
#, lua-format
-msgid "\n"
+msgid ""
+"\n"
"• Description: %s"
msgstr ""
@@ -1722,13 +2002,16 @@ msgstr ""
#: lua/groupbutler/plugins/report.lua:116
#, lua-format
-msgid "*New parameters saved*.\n"
+msgid ""
+"*New parameters saved*.\n"
"Users will be able to use @admin %d times/%d minutes"
msgstr ""
#: lua/groupbutler/plugins/report.lua:137
#, lua-format
-msgid "_Please, do not abuse this command. It can be used %d times every %d minutes_.\n"
+msgid ""
+"_Please, do not abuse this command. It can be used %d times every %d "
+"minutes_.\n"
"Wait other %d minutes, %d seconds."
msgstr ""
@@ -1738,7 +2021,8 @@ msgid "_Reported to %d admin(s)_"
msgstr ""
#: lua/groupbutler/plugins/report.lua:165
-msgid "You closed this issue and deleted all the other reports sent to the admins"
+msgid ""
+"You closed this issue and deleted all the other reports sent to the admins"
msgstr ""
#: lua/groupbutler/plugins/report.lua:173
@@ -1759,7 +2043,9 @@ msgid "%s has/will address this report"
msgstr ""
#: lua/groupbutler/plugins/report.lua:204
-msgid "This button will delete all the reports sent to the other admins. Tap it again to confirm"
+msgid ""
+"This button will delete all the reports sent to the other admins. Tap it "
+"again to confirm"
msgstr ""
#: lua/groupbutler/plugins/report.lua:212
@@ -1771,7 +2057,9 @@ msgid "🚫 Unknown or non-existent group"
msgstr ""
#: lua/groupbutler/plugins/rules.lua:45
-msgid "🚷 You are not a member of this chat. You can't read the rules of a private group."
+msgid ""
+"🚷 You are not a member of this chat. You can't read the rules of a private "
+"group."
msgstr ""
#: lua/groupbutler/plugins/rules.lua:75
@@ -1797,12 +2085,16 @@ msgstr ""
#: lua/groupbutler/plugins/service.lua:66
#, lua-format
-msgid "Hello everyone!\n"
-"My name is %s, and I'm a bot made to help administrators in their hard work.\n"
+msgid ""
+"Hello everyone!\n"
+"My name is %s, and I'm a bot made to help administrators in their hard "
+"work.\n"
msgstr ""
#: lua/groupbutler/plugins/service.lua:70
-msgid "Yay! This group has been upgraded. You are great! Now I can work properly :)\n"
+msgid ""
+"Yay! This group has been upgraded. You are great! Now I can work "
+"properly :)\n"
msgstr ""
#: lua/groupbutler/plugins/setlang.lua:33
@@ -1820,149 +2112,143 @@ msgid "English language is *set*"
msgstr ""
#: lua/groupbutler/plugins/setlang.lua:59
-msgid ".\n"
-"Please note that translators are volunteers, and this localization _may be incomplete_. You can help improve translations on our [Crowdin Project](https://crowdin.com/project/group-butler).\n"
+msgid ""
+".\n"
+"Please note that translators are volunteers, and this localization _may be "
+"incomplete_. You can help improve translations on our [Crowdin Project]"
+"(https://crowdin.com/project/group-butler).\n"
msgstr ""
-#: lua/groupbutler/plugins/users.lua:17
+#: lua/groupbutler/plugins/users.lua:23
msgid "can't change the chat title/description/icon"
msgstr ""
-#: lua/groupbutler/plugins/users.lua:18
+#: lua/groupbutler/plugins/users.lua:24
msgid "can't send messages"
msgstr ""
-#: lua/groupbutler/plugins/users.lua:19
+#: lua/groupbutler/plugins/users.lua:25
msgid "can't delete messages"
msgstr ""
-#: lua/groupbutler/plugins/users.lua:20
+#: lua/groupbutler/plugins/users.lua:26
msgid "can't invite users/generate a link"
msgstr ""
-#: lua/groupbutler/plugins/users.lua:21
+#: lua/groupbutler/plugins/users.lua:27
msgid "can't restrict members"
msgstr ""
-#: lua/groupbutler/plugins/users.lua:22
+#: lua/groupbutler/plugins/users.lua:28
msgid "can't pin messages"
msgstr ""
-#: lua/groupbutler/plugins/users.lua:23
+#: lua/groupbutler/plugins/users.lua:29
msgid "can't promote new admins"
msgstr ""
-#: lua/groupbutler/plugins/users.lua:24
+#: lua/groupbutler/plugins/users.lua:30
msgid "can't send photos/videos/documents/audios/voice messages/video messages"
msgstr ""
-#: lua/groupbutler/plugins/users.lua:25
+#: lua/groupbutler/plugins/users.lua:31
msgid "can't send stickers/GIFs/games/use inline bots"
msgstr ""
-#: lua/groupbutler/plugins/users.lua:26
+#: lua/groupbutler/plugins/users.lua:32
msgid "can't show link previews"
msgstr ""
-#: lua/groupbutler/plugins/users.lua:32
+#: lua/groupbutler/plugins/users.lua:38
msgid "🔄️ Refresh cache"
msgstr ""
-#: lua/groupbutler/plugins/users.lua:60
+#: lua/groupbutler/plugins/users.lua:46
msgid "Remove warnings"
msgstr ""
-#: lua/groupbutler/plugins/users.lua:70
+#: lua/groupbutler/plugins/users.lua:56
#, lua-format
-msgid "*User ID*: `%d`\n"
+msgid ""
+"*User ID*: `%d`\n"
"`Warnings`: *%d*\n"
"`Media warnings`: *%d*\n"
"`Spam warnings`: *%d*\n"
msgstr ""
-#: lua/groupbutler/plugins/users.lua:90
+#: lua/groupbutler/plugins/users.lua:76
#, lua-format
msgid "Your ID is `%d`"
+#: lua/groupbutler/plugins/users.lua:109
msgstr ""
-#: lua/groupbutler/plugins/users.lua:124
-msgid "That user has nothing to do with this chat"
-msgstr ""
-
-#: lua/groupbutler/plugins/users.lua:131
#, lua-format
msgid "%s is banned from this group"
msgstr ""
-#: lua/groupbutler/plugins/users.lua:132
+#: lua/groupbutler/plugins/users.lua:110
#, lua-format
msgid "%s left the group or has been kicked and unbanned"
msgstr ""
-#: lua/groupbutler/plugins/users.lua:133
+#: lua/groupbutler/plugins/users.lua:111
#, lua-format
msgid "%s is an admin"
msgstr ""
-#: lua/groupbutler/plugins/users.lua:134
+#: lua/groupbutler/plugins/users.lua:112
#, lua-format
msgid "%s is the group creator"
msgstr ""
-#: lua/groupbutler/plugins/users.lua:135
+#: lua/groupbutler/plugins/users.lua:113
#, lua-format
msgid "%s has nothing to do with this chat"
msgstr ""
-#: lua/groupbutler/plugins/users.lua:136
+#: lua/groupbutler/plugins/users.lua:114
#, lua-format
msgid "%s is a chat member"
msgstr ""
-#: lua/groupbutler/plugins/users.lua:137
+#: lua/groupbutler/plugins/users.lua:115
#, lua-format
msgid "%s is a restricted"
msgstr ""
-#: lua/groupbutler/plugins/users.lua:148
+#: lua/groupbutler/plugins/users.lua:127
#, lua-format
-msgid "\n"
+msgid ""
+"\n"
"Restrictions: %s"
msgstr ""
-#: lua/groupbutler/plugins/users.lua:159
+#: lua/groupbutler/plugins/users.lua:138
msgid "Reply to a user or mention them by username or numerical ID"
msgstr ""
-#: lua/groupbutler/plugins/users.lua:183 lua/groupbutler/plugins/users.lua:244
+#: lua/groupbutler/plugins/users.lua:156
#, lua-format
-msgid "📌 Status: `CACHED`\n"
-"⌛ ️Remaining: `%s`\n"
-"👥 Admins cached: `%d`"
+msgid "👥 Admins cached: %d
"
msgstr ""
-#: lua/groupbutler/plugins/users.lua:192
+#: lua/groupbutler/plugins/users.lua:164
#, lua-format
msgid "Message N° %d"
msgstr ""
-#: lua/groupbutler/plugins/users.lua:214 lua/groupbutler/plugins/warn.lua:167
+#: lua/groupbutler/plugins/users.lua:186 lua/groupbutler/plugins/warn.lua:170
msgid "You are not allowed to use this button"
msgstr ""
-#: lua/groupbutler/plugins/users.lua:223
+#: lua/groupbutler/plugins/users.lua:194
#, lua-format
-msgid "The number of warnings received by this user has been reset, by %s"
-msgstr ""
-
-#: lua/groupbutler/plugins/users.lua:237
-#, lua-format
-msgid "The adminlist has just been updated. You must wait 10 minutes from the last refresh (wait %d seconds)"
+msgid ""
+"The number of warnings received by this user has been reset, by %s"
msgstr ""
-#: lua/groupbutler/plugins/users.lua:246
-#, lua-format
-msgid "✅ Updated. Next update in %s"
+#: lua/groupbutler/plugins/users.lua:205
+msgid "✅ The admin list will be updated soon"
msgstr ""
#: lua/groupbutler/plugins/warn.lua:18
@@ -1979,7 +2265,8 @@ msgstr ""
#: lua/groupbutler/plugins/warn.lua:55
#, lua-format
-msgid "*Old* value was %d\n"
+msgid ""
+"*Old* value was %d\n"
"*New* max is %d"
msgstr ""
@@ -1992,376 +2279,386 @@ msgid "No"
msgstr ""
#: lua/groupbutler/plugins/warn.lua:68
-msgid "Do you want to continue and reset *all* the warnings received by *all* the users of the group?"
+msgid ""
+"Do you want to continue and reset *all* the warnings received by *all* the "
+"users of the group?"
msgstr ""
-#: lua/groupbutler/plugins/warn.lua:86
+#: lua/groupbutler/plugins/warn.lua:89
#, lua-format
msgid "Done! %s has been forgiven."
msgstr ""
-#: lua/groupbutler/plugins/warn.lua:111
+#: lua/groupbutler/plugins/warn.lua:112
#, lua-format
msgid "%s %s: reached the max number of warnings (%d/%d
)"
msgstr ""
-#: lua/groupbutler/plugins/warn.lua:144
+#: lua/groupbutler/plugins/warn.lua:145
#, lua-format
msgid "%s has been warned (%d/%d
)"
msgstr ""
-#: lua/groupbutler/plugins/warn.lua:175
+#: lua/groupbutler/plugins/warn.lua:181
msgid "The number of warnings received by this user is already zero"
msgstr ""
-#: lua/groupbutler/plugins/warn.lua:179
+#: lua/groupbutler/plugins/warn.lua:185
#, lua-format
msgid "Warn removed! (%d/%d)"
msgstr ""
-#: lua/groupbutler/plugins/warn.lua:182
+#: lua/groupbutler/plugins/warn.lua:188
#, lua-format
-msgid "\n"
+msgid ""
+"\n"
"(Admin: %s)"
msgstr ""
-#: lua/groupbutler/plugins/warn.lua:191
+#: lua/groupbutler/plugins/warn.lua:198
#, lua-format
msgid "Done. All the warnings of this group have been erased by %s"
msgstr ""
-#: lua/groupbutler/plugins/warn.lua:193
+#: lua/groupbutler/plugins/warn.lua:200
msgid "_Action aborted_"
msgstr ""
-#: lua/groupbutler/plugins/welcome.lua:83
+#: lua/groupbutler/plugins/welcome.lua:71
msgid "Read the rules"
msgstr ""
-#: lua/groupbutler/plugins/welcome.lua:106
+#: lua/groupbutler/plugins/welcome.lua:94
msgid "Hi $name!"
msgstr ""
-#: lua/groupbutler/plugins/welcome.lua:155
+#: lua/groupbutler/plugins/welcome.lua:146
msgid "Welcome and...?"
msgstr ""
-#: lua/groupbutler/plugins/welcome.lua:179
+#: lua/groupbutler/plugins/welcome.lua:170
#, lua-format
msgid "A form of media has been set as the welcome message: `%s`"
msgstr ""
-#: lua/groupbutler/plugins/welcome.lua:181
+#: lua/groupbutler/plugins/welcome.lua:172
msgid "Reply to a `sticker` or a `gif` to set them as the *welcome message*"
msgstr ""
-#: lua/groupbutler/plugins/welcome.lua:200
+#: lua/groupbutler/plugins/welcome.lua:191
msgid "*Custom welcome message saved!*"
msgstr ""
-#: lua/groupbutler/utilities.lua:515
+#: lua/groupbutler/utilities.lua:397
msgid "-*empty*-"
msgstr ""
-#: lua/groupbutler/utilities.lua:556
+#: lua/groupbutler/utilities.lua:438
#, lua-format
-msgid "👤 Creator\n"
-"└ %s\n\n"
+msgid ""
+"👤 Creator\n"
+"└ %s\n"
+"\n"
"👥 Admins (%d)\n"
"%s"
msgstr ""
-#: lua/groupbutler/utilities.lua:566
+#: lua/groupbutler/utilities.lua:448
msgid "No commands set"
msgstr ""
-#: lua/groupbutler/utilities.lua:569
+#: lua/groupbutler/utilities.lua:451
msgid "List of custom commands:\n"
msgstr ""
-#: lua/groupbutler/utilities.lua:581
-msgid "Current settings for *the group*:\n\n"
+#: lua/groupbutler/utilities.lua:463
+msgid ""
+"Current settings for *the group*:\n"
+"\n"
msgstr ""
-#: lua/groupbutler/utilities.lua:582
+#: lua/groupbutler/utilities.lua:464
#, lua-format
msgid "*Language*: %s\n"
msgstr ""
-#: lua/groupbutler/utilities.lua:586
+#: lua/groupbutler/utilities.lua:468
msgid "Welcome message"
msgstr ""
-#: lua/groupbutler/utilities.lua:587
+#: lua/groupbutler/utilities.lua:469
msgid "Goodbye message"
msgstr ""
-#: lua/groupbutler/utilities.lua:597
+#: lua/groupbutler/utilities.lua:479
msgid "Welcome button"
msgstr ""
-#: lua/groupbutler/utilities.lua:599
+#: lua/groupbutler/utilities.lua:481
msgid "Unknown"
msgstr ""
-#: lua/groupbutler/utilities.lua:634
+#: lua/groupbutler/utilities.lua:516
msgid "*Welcome type*: `GIF / sticker`\n"
msgstr ""
-#: lua/groupbutler/utilities.lua:636
+#: lua/groupbutler/utilities.lua:518
msgid "*Welcome type*: `custom message`\n"
msgstr ""
-#: lua/groupbutler/utilities.lua:638
+#: lua/groupbutler/utilities.lua:520
msgid "*Welcome type*: `default message`\n"
msgstr ""
-#: lua/groupbutler/utilities.lua:647
+#: lua/groupbutler/utilities.lua:529
#, lua-format
msgid "Warns (`standard`): *%s*\n"
msgstr ""
-#: lua/groupbutler/utilities.lua:648
+#: lua/groupbutler/utilities.lua:530
#, lua-format
-msgid "Warns (`media`): *%s*\n\n"
+msgid ""
+"Warns (`media`): *%s*\n"
+"\n"
msgstr ""
-#: lua/groupbutler/utilities.lua:649
+#: lua/groupbutler/utilities.lua:531
msgid "✅ = _enabled / allowed_\n"
msgstr ""
-#: lua/groupbutler/utilities.lua:650
+#: lua/groupbutler/utilities.lua:532
msgid "🚫 = _disabled / not allowed_\n"
msgstr ""
-#: lua/groupbutler/utilities.lua:651
+#: lua/groupbutler/utilities.lua:533
msgid "👥 = _sent in group (always for admins)_\n"
msgstr ""
-#: lua/groupbutler/utilities.lua:652
+#: lua/groupbutler/utilities.lua:534
msgid "👤 = _sent in private_"
msgstr ""
-#: lua/groupbutler/utilities.lua:662
+#: lua/groupbutler/utilities.lua:544
msgid "@admin command disabled"
msgstr ""
-#: lua/groupbutler/utilities.lua:663
+#: lua/groupbutler/utilities.lua:545
msgid "Welcome message won't be displayed from now"
msgstr ""
-#: lua/groupbutler/utilities.lua:664
+#: lua/groupbutler/utilities.lua:546
msgid "Goodbye message won't be displayed from now"
msgstr ""
-#: lua/groupbutler/utilities.lua:665
+#: lua/groupbutler/utilities.lua:547
msgid "#extra commands are now available only for administrators"
msgstr ""
-#: lua/groupbutler/utilities.lua:666
+#: lua/groupbutler/utilities.lua:548
msgid "Anti-flood is now off"
msgstr ""
-#: lua/groupbutler/utilities.lua:667
+#: lua/groupbutler/utilities.lua:549
msgid "/rules will reply in private (for users)"
msgstr ""
-#: lua/groupbutler/utilities.lua:668
+#: lua/groupbutler/utilities.lua:550
msgid "Silent mode is now off"
msgstr ""
-#: lua/groupbutler/utilities.lua:669
+#: lua/groupbutler/utilities.lua:551
msgid "Links preview disabled"
msgstr ""
-#: lua/groupbutler/utilities.lua:670
+#: lua/groupbutler/utilities.lua:552
msgid "Welcome message without a button for the rules"
msgstr ""
-#: lua/groupbutler/utilities.lua:673
+#: lua/groupbutler/utilities.lua:555
msgid "@admin command enabled"
msgstr ""
-#: lua/groupbutler/utilities.lua:674
+#: lua/groupbutler/utilities.lua:556
msgid "Welcome message will be displayed"
msgstr ""
-#: lua/groupbutler/utilities.lua:675
+#: lua/groupbutler/utilities.lua:557
msgid "Goodbye message will be displayed"
msgstr ""
-#: lua/groupbutler/utilities.lua:676
+#: lua/groupbutler/utilities.lua:558
msgid "#extra commands are now available for all"
msgstr ""
-#: lua/groupbutler/utilities.lua:677
+#: lua/groupbutler/utilities.lua:559
msgid "Anti-flood is now on"
msgstr ""
-#: lua/groupbutler/utilities.lua:678
+#: lua/groupbutler/utilities.lua:560
msgid "/rules will reply in the group (with everyone)"
msgstr ""
-#: lua/groupbutler/utilities.lua:679
+#: lua/groupbutler/utilities.lua:561
msgid "Silent mode is now on"
msgstr ""
-#: lua/groupbutler/utilities.lua:680
+#: lua/groupbutler/utilities.lua:562
msgid "Links preview enabled"
msgstr ""
-#: lua/groupbutler/utilities.lua:681
+#: lua/groupbutler/utilities.lua:563
msgid "The welcome message will have a button for the rules"
msgstr ""
-#: lua/groupbutler/utilities.lua:694
-msgid "This setting is enabled, but the goodbye message won't be displayed in large groups, because I can't see service messages about left members"
+#: lua/groupbutler/utilities.lua:576
+msgid ""
+"This setting is enabled, but the goodbye message won't be displayed in large "
+"groups, because I can't see service messages about left members"
msgstr ""
-#: lua/groupbutler/utilities.lua:707
+#: lua/groupbutler/utilities.lua:589
msgid "Start me"
msgstr ""
-#: lua/groupbutler/utilities.lua:709
+#: lua/groupbutler/utilities.lua:591
msgid "_Please message me first so I can message you_"
msgstr ""
-#: lua/groupbutler/utilities.lua:778 lua/groupbutler/utilities.lua:779
-msgid "Someone"
-msgstr ""
-
-#: lua/groupbutler/utilities.lua:787
-msgid "Reply to a user or mention them"
-msgstr ""
-
-#: lua/groupbutler/utilities.lua:813
-msgid "I've never seen this user before.\n"
-"This command works by reply, username, user ID or text mention.\n"
-"If you're using it by username and want to teach me who the user is, forward me one of their messages"
-msgstr ""
-
-#: lua/groupbutler/utilities.lua:832
+#: lua/groupbutler/utilities.lua:651
#, lua-format
msgid "Chat: %s [#chat%d]"
msgstr ""
-#: lua/groupbutler/utilities.lua:841 lua/groupbutler/utilities.lua:849
+#: lua/groupbutler/utilities.lua:660 lua/groupbutler/utilities.lua:668
#, lua-format
-msgid "#%s (%d/%d
), %s\n"
+msgid ""
+"#%s (%d/%d
), %s\n"
"• %s\n"
"• User: %s"
msgstr ""
-#: lua/groupbutler/utilities.lua:855
+#: lua/groupbutler/utilities.lua:674
#, lua-format
-msgid "#%s\n"
+msgid ""
+"#%s\n"
"• %s\n"
"• User: %s"
msgstr ""
-#: lua/groupbutler/utilities.lua:858 lua/groupbutler/utilities.lua:868
-#: lua/groupbutler/utilities.lua:871 lua/groupbutler/utilities.lua:874
+#: lua/groupbutler/utilities.lua:677 lua/groupbutler/utilities.lua:687
+#: lua/groupbutler/utilities.lua:690 lua/groupbutler/utilities.lua:693
#, lua-format
-msgid "%s\n"
+msgid ""
+"%s\n"
"• %s\n"
"• By: %s"
msgstr ""
-#: lua/groupbutler/utilities.lua:861
+#: lua/groupbutler/utilities.lua:680
msgid "Get the new photo"
msgstr ""
-#: lua/groupbutler/utilities.lua:878
+#: lua/groupbutler/utilities.lua:697
#, lua-format
-msgid "%s\n"
+msgid ""
+"%s\n"
"• %s\n"
"• By: %s\n"
"• Reported to %d admin(s)"
msgstr ""
-#: lua/groupbutler/utilities.lua:882
+#: lua/groupbutler/utilities.lua:701
#, lua-format
-msgid "#%s\n"
+msgid ""
+"#%s\n"
"• %s\n"
"• User: %s [#id%d]"
msgstr ""
-#: lua/groupbutler/utilities.lua:887
+#: lua/groupbutler/utilities.lua:706
#, lua-format
-msgid "%s\n"
+msgid ""
+"%s\n"
"• %s\n"
"• User: %s"
msgstr ""
-#: lua/groupbutler/utilities.lua:889
+#: lua/groupbutler/utilities.lua:708
#, lua-format
-msgid "\n"
+msgid ""
+"\n"
"• Added by: %s [#id%d]"
msgstr ""
-#: lua/groupbutler/utilities.lua:902
+#: lua/groupbutler/utilities.lua:721
#, lua-format
-msgid "#%s\n"
+msgid ""
+"#%s\n"
"• Admin: %s [#id%d]\n"
"• %s\n"
"• User: %s [#id%d]\n"
"• Count: %d/%d
"
msgstr ""
-#: lua/groupbutler/utilities.lua:910 lua/groupbutler/utilities.lua:940
+#: lua/groupbutler/utilities.lua:729 lua/groupbutler/utilities.lua:759
#, lua-format
-msgid "#%s\n"
+msgid ""
+"#%s\n"
"• Admin: %s [#id%s]\n"
"• %s\n"
"• User: %s [#id%s]"
msgstr ""
-#: lua/groupbutler/utilities.lua:914
+#: lua/groupbutler/utilities.lua:733
#, lua-format
-msgid "#%s\n"
+msgid ""
+"#%s\n"
"• Admin: %s [#id%s]\n"
"• %s\n"
msgstr ""
-#: lua/groupbutler/utilities.lua:917
+#: lua/groupbutler/utilities.lua:736
#, lua-format
msgid "• Users involved: %d"
msgstr ""
-#: lua/groupbutler/utilities.lua:919
+#: lua/groupbutler/utilities.lua:738
#, lua-format
msgid "• User: %s [#id%d]"
msgstr ""
-#: lua/groupbutler/utilities.lua:931
+#: lua/groupbutler/utilities.lua:750
#, lua-format
-msgid "#%s\n"
+msgid ""
+"#%s\n"
"• Admin: %s [#id%s]\n"
"• %s\n"
"• User: %s [#id%s]\n"
"• Duration: %d days, %d hours"
msgstr ""
-#: lua/groupbutler/utilities.lua:944
+#: lua/groupbutler/utilities.lua:763
#, lua-format
-msgid "#%s\n"
+msgid ""
+"#%s\n"
"• %s\n"
"• By: %s"
msgstr ""
-#: lua/groupbutler/utilities.lua:965
+#: lua/groupbutler/utilities.lua:784
#, lua-format
-msgid "\n"
+msgid ""
+"\n"
"• Action: #%s"
msgstr ""
-#: lua/groupbutler/utilities.lua:968
+#: lua/groupbutler/utilities.lua:787
#, lua-format
-msgid "\n"
+msgid ""
+"\n"
"• Reason: %s"
msgstr ""
-#: lua/groupbutler/utilities.lua:974
+#: lua/groupbutler/utilities.lua:793
msgid "Go to the message"
msgstr ""
-
diff --git a/lua/groupbutler/chat.lua b/lua/groupbutler/chat.lua
index e1e246811..57d5b5d2d 100644
--- a/lua/groupbutler/chat.lua
+++ b/lua/groupbutler/chat.lua
@@ -1,3 +1,5 @@
+local log = require("groupbutler.logging")
+
local Chat = {}
local function p(self)
@@ -5,14 +7,45 @@ local function p(self)
end
function Chat:new(obj, private)
+ assert(obj.id, "Chat: Missing obj.id")
+ assert(private.api, "Chat: Missing private.api")
assert(private.db, "Chat: Missing private.db")
setmetatable(obj, {
- __index = self,
+ __index = function(s, index)
+ if self[index] then
+ return self[index]
+ end
+ return s:getProperty(index)
+ end,
__private = private,
})
return obj
end
+function Chat:getProperty(index)
+ local property = rawget(self, index)
+ if property == nil then
+ property = p(self).db:getChatProperty(self, index)
+ if property == nil then
+ local ok = p(self).api:getChat(self.id)
+ if not ok then
+ log.warn("Chat: Failed to get {property} for {id}", {
+ property = index,
+ id = self.id,
+ })
+ return nil
+ end
+ for k,v in pairs(ok) do
+ self[k] = v
+ end
+ self:cache()
+ property = rawget(self, index)
+ end
+ self[index] = property
+ end
+ return property
+end
+
function Chat:cache()
p(self).db:cacheChat(self)
end
diff --git a/lua/groupbutler/chatmember.lua b/lua/groupbutler/chatmember.lua
new file mode 100644
index 000000000..40b158228
--- /dev/null
+++ b/lua/groupbutler/chatmember.lua
@@ -0,0 +1,107 @@
+local log = require("groupbutler.logging")
+local User = require("groupbutler.user")
+
+local ChatMember = {}
+
+local function p(self)
+ return getmetatable(self).__private
+end
+
+function ChatMember:new(obj, private)
+ assert(obj.chat, "ChatMember: Missing obj.chat")
+ assert(obj.user, "ChatMember: Missing obj.user")
+ assert(private.db, "ChatMember: Missing private.db")
+ assert(private.api, "ChatMember: Missing private.api")
+ setmetatable(obj, {
+ __index = function(s, index)
+ if self[index] then
+ return self[index]
+ end
+ return s:getProperty(index)
+ end,
+ __private = private,
+ })
+ return obj
+end
+
+function ChatMember:getProperty(index)
+ local property = rawget(self, index)
+ if property == nil then
+ property = p(self).db:getChatMemberProperty(self, index)
+ if property == nil then
+ local ok = p(self).api:getChatMember(self.chat.id, self.user.id)
+ if not ok then
+ log.warn("ChatMember: Failed to get {property} for {chat_id}, {user_id}", {
+ property = index,
+ chat_id = self.chat.id,
+ user_id = self.user.id,
+ })
+ return nil
+ end
+ for k,v in pairs(ok) do
+ self[k] = v
+ if k == "user" then
+ User:new(self.user, p(self))
+ end
+ end
+ self:cache()
+ property = rawget(self, index)
+ end
+ self[index] = property
+ end
+ return property
+end
+
+function ChatMember:cache()
+ p(self).db:cacheChatMember(self)
+end
+
+function ChatMember:isAdmin()
+ if self.chat.type == "private" then -- This should never happen but...
+ return false
+ end
+ return self.status == "creator" or self.status == "administrator"
+end
+
+function ChatMember:can(permission)
+ if self.chat.type == "private" then -- This should never happen but...
+ return false
+ end
+ if self.status == "creator"
+ or (self.status == "administrator" and self[permission]) then
+ return true
+ end
+ return false
+end
+
+function ChatMember:ban(until_date)
+ local ok, err = p(self).api:kickChatMember(self.chat.id, self.user.id, until_date)
+ if not ok then
+ return nil, p(self).api_err:trans(err)
+ end
+ return ok
+end
+
+function ChatMember:kick()
+ local ok, err = p(self).api:kickChatMember(self.chat.id, self.user.id)
+ if not ok then
+ return nil, p(self).api_err:trans(err)
+ end
+ p(self).api:unbanChatMember(self.chat.id, self.user.id)
+ return ok
+end
+
+function ChatMember:mute(until_date)
+ local ok, err = p(self).api:restrictChatMember({
+ chat_id = self.chat.id,
+ user_id = self.user.id,
+ until_date = until_date,
+ can_send_messages = false,
+ })
+ if not ok then
+ return nil, p(self).api_err:trans(err)
+ end
+ return ok
+end
+
+return ChatMember
diff --git a/lua/groupbutler/main.lua b/lua/groupbutler/main.lua
index 885ce4580..435dd9495 100644
--- a/lua/groupbutler/main.lua
+++ b/lua/groupbutler/main.lua
@@ -7,6 +7,7 @@ local plugins = require "groupbutler.plugins"
local Message = require "groupbutler.message"
local User = require "groupbutler.user"
local Chat = require "groupbutler.chat"
+local ChatMember = require "groupbutler.chatmember"
local storage = require "groupbutler.storage"
local locale = require "groupbutler.languages"
local api_err = require "groupbutler.api_errors"
@@ -41,21 +42,43 @@ function _M:new(update_obj)
end
local function inject_message_methods(message, update)
+ if message.from then
+ message.from = {
+ user = message.from,
+ chat = message.chat,
+ }
+ end
Message:new(message, update)
- if message.from then -- Sender is empty for messages sent to channels
- User:new(message.from, update):cache()
+ if message.from then
+ if message.from.user then -- Sender is empty for messages sent to channels
+ User:new(message.from.user, update):cache()
+ end
+ if message.from.chat then
+ Chat:new(message.chat, update)--:cache()
+ end
+ if message.from.user and message.from.chat then
+ ChatMember:new(message.from, update)--:cache()
+ end
end
if message.forward_from then
User:new(message.forward_from, update):cache()
end
- if message.chat then
- Chat:new(message.chat, update)
+ if message.new_chat_members then
+ for k,v in pairs(message.new_chat_members) do
+ message.new_chat_members[k] = {
+ user = v,
+ chat = message.chat,
+ }
+ User:new(message.new_chat_members[k].user, update)
+ Chat:new(message.new_chat_members[k].chat, update)
+ ChatMember:new(message.new_chat_members[k], update):cache()
+ end
end
end
local function add_message_methods(object, update)
local message_objects = {
- "message", "edited_message", "channel_post", -- Possible messages inside updates
+ "message", --[["edited_message",]] "channel_post", -- Possible messages inside updates
"reply_to_message", "pinned_message", -- Possible messages inside messages
}
for _, message in pairs(message_objects) do
@@ -75,9 +98,9 @@ local function collect_stats(self)
local red = self.red
local u = self.u
local now = os.time(os.date("*t"))
- if msg.chat.type ~= 'private' and msg.chat.type ~= 'inline' and msg.from then
- red:hset('chat:'..msg.chat.id..':userlast', msg.from.id, now) --last message for each user
- red:hset('bot:chats:latsmsg', msg.chat.id, now) --last message in the group
+ if msg.from.chat.type ~= 'private' and msg.from.chat.type ~= 'inline' and msg.from.user then
+ red:hset('chat:'..msg.from.chat.id..':userlast', msg.from.user.id, now) --last message for each user
+ red:hset('bot:chats:latsmsg', msg.from.chat.id, now) --last message in the group
end
u:metric_incr("messages_processed_count")
u:metric_set("message_timestamp_distance_sec", now - msg.date)
@@ -121,21 +144,21 @@ local function on_msg_receive(self, callback) -- The fn run whenever a message i
end
-- Set chat language
- i18n:setLanguage(red:get('lang:'..msg.chat.id))
+ i18n:setLanguage(red:get('lang:'..msg.from.chat.id))
-- Do not process messages from normal groups
- if msg.chat.type == 'group' then
- api:sendMessage(msg.chat.id, i18n([[Hello everyone!
+ if msg.from.chat.type == 'group' then
+ api:sendMessage(msg.from.chat.id, i18n([[Hello everyone!
My name is %s, and I'm a bot made to help administrators in their hard work.
Unfortunately I can't work in normal groups. If you need me, please ask the creator to convert this group to a supergroup and then add me again.
]]):format(bot.first_name))
- api:leaveChat(msg.chat.id)
+ api:leaveChat(msg.from.chat.id)
if config.bot_settings.stream_commands then
log.info('Bot was added to a normal group {by_name} [{from_id}] -> [{chat_id}]',
{
- by_name=msg.from.first_name,
- from_id=msg.from.id,
- chat_id=msg.chat.id,
+ by_name=msg.from.user.first_name,
+ from_id=msg.from.user.id,
+ chat_id=msg.from.chat.id,
})
end
return true
@@ -167,11 +190,11 @@ Unfortunately I can't work in normal groups. If you need me, please ask the crea
if blocks then
-- init agroup if the bot wasn't aware to be in
- if msg.chat.id < 0
- and msg.chat.type ~= 'inline'
- and red:exists('chat:'..msg.chat.id..':settings') == 0
+ if msg.from.chat.id < 0
+ and msg.from.chat.type ~= 'inline'
+ and red:exists('chat:'..msg.from.chat.id..':settings') == 0
and not msg.service then
- u:initGroup(msg.chat.id)
+ u:initGroup(msg.from.chat)
end
-- print some info in the terminal
@@ -179,9 +202,9 @@ Unfortunately I can't work in normal groups. If you need me, please ask the crea
log.info('{trigger} {from_name} [{from_id}] -> [{chat_id}]',
{
trigger=trigger,
- from_name=msg.from.first_name,
- from_id=msg.from.id,
- chat_id=msg.chat.id,
+ from_name=msg.from.user.first_name,
+ from_id=msg.from.user.id,
+ chat_id=msg.from.chat.id,
})
end
@@ -230,7 +253,7 @@ function _M:process()
function_key = 'onEditedMessage'
end
- self.message = self.message or self.edited_message
+ self.message = self.message or self.edited_message -- TODO: undo this
local service_messages = {
"left_chat_member", "new_chat_member", "new_chat_photo", "delete_chat_photo", "group_chat_created",
@@ -291,7 +314,11 @@ function _M:process()
self.message.message_id = self.message.message.message_id
self.message.chat = self.message.message.chat
else --when the inline keyboard is sent via the inline mode
- self.message.chat = {type = 'inline', id = self.message.from.id, title = self.message.from.first_name}
+ self.message.chat = {
+ type = 'inline',
+ id = self.message.from.user.id,
+ title = self.message.from.user.first_name
+ }
self.message.message_id = self.message.inline_message_id
end
self.message.date = os.time()
diff --git a/lua/groupbutler/message.lua b/lua/groupbutler/message.lua
index ebfad84b7..666df2ff5 100644
--- a/lua/groupbutler/message.lua
+++ b/lua/groupbutler/message.lua
@@ -1,26 +1,21 @@
-local message = {}
+local User = require("groupbutler.user")
+local ChatMember = require("groupbutler.chatmember")
-function message:new(message_obj, update_obj)
- message_obj.api = update_obj.api
- message_obj.u = update_obj.u
- setmetatable(message_obj, {__index = self})
- return message_obj
-end
+local Message = {}
+local message = Message
-local function is_from_admin(self)
- if self.chat.type == "private" -- This should never happen but...
- or not (self.chat.id < 0 or self.target_id)
- or not self.from then
- return false
- end
- return self.u:is_admin(self.target_id or self.chat.id, self.from.id)
+local function p(self)
+ return getmetatable(self).__private
end
-function message:is_from_admin()
- if self._cached_is_from_admin == nil then
- self._cached_is_from_admin = is_from_admin(self)
- end
- return self._cached_is_from_admin
+function Message:new(obj, private)
+ assert(private.api, "Message: Missing private.api")
+ assert(private.i18n, "Message: Missing private.i18n")
+ setmetatable(obj, {
+ __index = self,
+ __private = private,
+ })
+ return obj
end
local function msg_type(self)
@@ -75,9 +70,57 @@ function message:get_file_id()
end
function message:send_reply(text, parse_mode, disable_web_page_preview, disable_notification, reply_markup)
- local api = self.api
- return api:send_message(self.chat.id, text, parse_mode, disable_web_page_preview, disable_notification,
+ return p(self).api:sendMessage(self.chat.id, text, parse_mode, disable_web_page_preview, disable_notification,
self.message_id, reply_markup)
end
-return message
+function Message:getTargetMember(blocks) -- TODO: extract username/id from self.text or move blocks{} into self
+ if not self.reply_to_message
+ and (not blocks or not blocks[2]) then
+ return false, p(self).i18n("Reply to a user or mention them")
+ end
+
+ local user_not_found = p(self).i18n([[I've never seen this user before.
+This command works by reply, username, user ID or text mention.
+If you're using it by username and want to teach me who the user is, forward me one of their messages]])
+
+ if self.reply_to_message then
+ if self.reply_to_message.new_chat_member then
+ return ChatMember:new({
+ user = self.reply_to_message.new_chat_member,
+ chat = self.from.chat,
+ }, p(self))
+ end
+ return self.reply_to_message.from
+ end
+
+ if blocks[2]:byte(1) == string.byte("@") then
+ local user = User:new({username = blocks[2]}, p(self))
+ if not user then
+ return false, user_not_found
+ end
+ return ChatMember:new({
+ user = user,
+ chat = self.from.chat,
+ }, p(self))
+ end
+
+ if self.mention_id then
+ return ChatMember:new({
+ user = User:new({id=self.mention_id}, p(self)),
+ chat = self.from.chat,
+ }, p(self))
+ end
+
+ local id = blocks[2]:match("%d+")
+ if id then
+ return ChatMember:new({
+ user = User:new({id=id}, p(self)),
+ chat = self.from.chat,
+ }, p(self))
+ end
+
+ return false, user_not_found
+end
+
+return Message
diff --git a/lua/groupbutler/plugins/admin.lua b/lua/groupbutler/plugins/admin.lua
index 88ef88ad1..da23f4162 100644
--- a/lua/groupbutler/plugins/admin.lua
+++ b/lua/groupbutler/plugins/admin.lua
@@ -1,6 +1,8 @@
local config = require "groupbutler.config"
local null = require "groupbutler.null"
local json = require "cjson"
+local User = require("groupbutler.user")
+local Chat = require("groupbutler.chat")
local _M = {}
@@ -90,7 +92,7 @@ end
local function get_chat_id(msg)
if msg.text:find('$chat') then
- return msg.chat.id
+ return msg.from.chat.id
elseif msg.text:find('-%d+') then
return msg.text:match('(-%d+)')
elseif msg.reply then
@@ -108,7 +110,7 @@ function _M:onTextMessage(blocks)
local bot = self.bot
local u = self.u
local red = self.red
- if not u:is_superadmin(msg.from.id) then return end
+ if not u:is_superadmin(msg.from.user.id) then return end
for i=1, #triggers2 do
blocks = match_pattern(self, triggers2[i], msg.text)
@@ -118,7 +120,7 @@ function _M:onTextMessage(blocks)
if not blocks or not next(blocks) then return true end --leave this plugin and continue to match the others
if blocks[1] == 'admin' then
- api:sendMessage(msg.from.id, json.encode(triggers2))
+ api:sendMessage(msg.from.user.id, json.encode(triggers2))
end
-- if blocks[1] == 'init' then
-- local n_plugins = bot.init(true)
@@ -131,11 +133,11 @@ function _M:onTextMessage(blocks)
local cmd = io.popen('sudo tar -cpf '..bot.first_name:gsub(' ', '_')..'.tar *')
cmd:read('*all')
cmd:close()
- api:sendDocument(msg.from.id, './'..bot.first_name:gsub(' ', '_')..'.tar')
+ api:sendDocument(msg.from.user.id, './'..bot.first_name:gsub(' ', '_')..'.tar')
end
if blocks[1] == 'save' then
local res = red:save()
- api:sendMessage(msg.chat.id, 'res: '..tostring(res))
+ api:sendMessage(msg.from.chat.id, 'res: '..tostring(res))
end
if blocks[1] == 'stats' then
local text = '#stats `['..u:get_date()..']`:\n'
@@ -166,11 +168,11 @@ function _M:onTextMessage(blocks)
-- end
-- text = text..'- *ops/sec*: `'..dbinfo.stats.instantaneous_ops_per_sec..'`\n'
- api:sendMessage(msg.chat.id, text, "Markdown")
+ api:sendMessage(msg.from.chat.id, text, "Markdown")
end
-- if blocks[1] == 'lua' then
-- local output = load_lua(blocks[2], msg)
- -- api:sendMessage(msg.chat.id, output, "Markdown")
+ -- api:sendMessage(msg.from.chat.id, output, "Markdown")
-- end
if blocks[1] == 'run' then
--read the output
@@ -211,45 +213,45 @@ function _M:onTextMessage(blocks)
msg:send_reply(text)
end
if blocks[1] == 'blocked' then
- api:sendMessage(msg.chat.id, json.encode(red:smembers('bot:blocked')))
+ api:sendMessage(msg.from.chat.id, json.encode(red:smembers('bot:blocked')))
end
if blocks[1] == 'leave' then
local text
if not blocks[2] then
- if msg.chat.type == 'private' then
+ if msg.from.chat.type == 'private' then
text = 'ID missing'
else
- text = bot_leave(self, msg.chat.id)
+ text = bot_leave(self, msg.from.chat.id)
end
else
text = bot_leave(self, blocks[2])
end
- api:sendMessage(msg.from.id, text)
+ api:sendMessage(msg.from.user.id, text)
end
if blocks[1] == 'api errors' then
local t = red:array_to_hash(red:hgetall('bot:errors'))
- api:sendMessage(msg.chat.id, json.encode(t))
+ api:sendMessage(msg.from.chat.id, json.encode(t))
end
-- if blocks[1] == 'rediscli' then
-- local redis_f = blocks[2]:gsub(' ', '(\'', 1)
-- redis_f = redis_f:gsub(' ', '\',\'')
-- redis_f = 'return red:'..redis_f..'\')'
- -- redis_f = redis_f:gsub('$chat', msg.chat.id)
- -- redis_f = redis_f:gsub('$from', msg.from.id)
+ -- redis_f = redis_f:gsub('$chat', msg.from.chat.id)
+ -- redis_f = redis_f:gsub('$from', msg.from.user.id)
-- local output = load_lua(redis_f)
-- msg:send_reply(output, "Markdown")
-- end
if blocks[1] == 'sendfile' then
local path = blocks[2]
- api:sendDocument(msg.from.id, path)
+ api:sendDocument(msg.from.user.id, path)
end
if blocks[1] == 'res' then
- local username = blocks[2]
- local user_id = {
- normal = u:resolve_user(username),
- lower = u:resolve_user(username:lower())
- }
- api:sendMessage(msg.chat.id, json.encode(user_id))
+ local user = User:new({username=blocks[2]}, self)
+ if user then
+ msg:send_reply(user:getLink(), "html")
+ else
+ msg:send_reply("User not found")
+ end
end
if blocks[1] == 'tban' then
if blocks[2] == 'flush' then
@@ -257,14 +259,14 @@ function _M:onTextMessage(blocks)
msg:send_reply('Flushed!')
end
if blocks[2] == 'get' then
- api:sendMessage(msg.chat.id, json.encode(red:array_to_hash(red:hgetall('tempbanned'))))
+ api:sendMessage(msg.from.chat.id, json.encode(red:array_to_hash(red:hgetall('tempbanned'))))
end
end
if blocks[1] == 'remban' then
- local user_id = u:resolve_user(blocks[2])
+ local user = User:new({username = blocks[2]}, self)
local text
- if user_id then
- red:del('ban:'..user_id)
+ if user then
+ red:del('ban:'..user.id)
text = 'Done'
else
text = 'Username not stored'
@@ -274,7 +276,7 @@ function _M:onTextMessage(blocks)
if blocks[1] == 'rawinfo' then
local chat_id = blocks[2]
if blocks[2] == '$chat' then
- chat_id = msg.chat.id
+ chat_id = msg.from.chat.id
end
local text = ''..chat_id
for set, _ in pairs(config.chat_settings) do
@@ -286,12 +288,12 @@ function _M:onTextMessage(blocks)
text = text..'
'
- api:sendMessage(msg.chat.id, text, 'html')
+ api:sendMessage(msg.from.chat.id, text, 'html')
end
if blocks[1] == 'rawinfo2' then
local chat_id
if blocks[2] == '$chat' then
- chat_id = msg.chat.id
+ chat_id = msg.from.chat.id
else
chat_id = blocks[2]
end
@@ -308,18 +310,18 @@ function _M:onTextMessage(blocks)
text = text.."\n\n"..config.chat_sets[i]..'(set)>\n'..section
end
text = text..''
- local ok, err = api:sendMessage(msg.chat.id, text, 'html')
+ local ok, err = api:sendMessage(msg.from.chat.id, text, 'html')
if not ok and err.description:match("message is too long") then
local file_path = '/tmp/'..chat_id..'.txt'
local file = io.open(file_path, "w")
file:write(text)
file:close()
- api:sendDocument(msg.chat.id, file_path)
+ api:sendDocument(msg.from.chat.id, file_path)
end
end
if blocks[1] == 'initgroup' then
- u:initGroup(blocks[2])
- api:sendMessage(msg.chat.id, 'Done')
+ u:initGroup(Chat:new({id=blocks[2]}, self))
+ api:sendMessage(msg.from.chat.id, 'Done')
end
if blocks[1] == 'remgroup' then
local full = false
@@ -329,7 +331,7 @@ function _M:onTextMessage(blocks)
chat_id = blocks[3]
end
u:remGroup(chat_id)
- api:sendMessage(msg.chat.id, 'Removed (heavy: '..tostring(full)..')')
+ api:sendMessage(msg.from.chat.id, 'Removed (heavy: '..tostring(full)..')')
end
if blocks[1] == 'cache' then
local chat_id = get_chat_id(msg)
@@ -338,18 +340,18 @@ function _M:onTextMessage(blocks)
local permissions = red:smembers("cache:chat:"..chat_id..":"..members[i]..":permissions")
members[members[i]] = permissions
end
- api:sendMessage(msg.chat.id, chat_id..' ➤ '..tostring(#members)..'\n'..json.encode(members))
+ api:sendMessage(msg.from.chat.id, chat_id..' ➤ '..tostring(#members)..'\n'..json.encode(members))
end
if blocks[1] == 'initcache' then
local chat_id, text
chat_id = get_chat_id(msg)
- local res, code = u:cache_adminlist(chat_id)
+ local res, code = u:cache_adminlist(Chat:new({id=chat_id}, self))
if res then
text = 'Cached ➤ '..code..' admins stored'
else
text = 'Failed: '..tostring(code)
end
- api:sendMessage(msg.chat.id, text)
+ api:sendMessage(msg.from.chat.id, text)
end
if blocks[1] == 'active' then
local days = tonumber(blocks[2]) or 7
@@ -362,11 +364,11 @@ function _M:onTextMessage(blocks)
n = n + 1
end
end
- api:sendMessage(msg.chat.id, 'Active groups in the last '..days..' days: '..n)
+ api:sendMessage(msg.from.chat.id, 'Active groups in the last '..days..' days: '..n)
end
if blocks[1] == 'getid' then
if msg.reply.forward_from then
- api:sendMessage(msg.chat.id, '`'..msg.reply.forward_from.id..'`', true)
+ api:sendMessage(msg.from.chat.id, '`'..msg.reply.forward_from.id..'`', true)
end
end
if blocks[1] == 'permission' then
@@ -375,7 +377,7 @@ function _M:onTextMessage(blocks)
api:sendMessage(msg, "Can't find a chat_id")
else
local res = api:getChatMember(chat_id, bot.id)
- api:sendMessage(msg.chat.id, ('%s\n%s'):format(chat_id, json.encode(res)))
+ api:sendMessage(msg.from.chat.id, ('%s\n%s'):format(chat_id, json.encode(res)))
end
end
end
diff --git a/lua/groupbutler/plugins/antispam.lua b/lua/groupbutler/plugins/antispam.lua
index 2499a187c..2611c9241 100644
--- a/lua/groupbutler/plugins/antispam.lua
+++ b/lua/groupbutler/plugins/antispam.lua
@@ -1,5 +1,7 @@
local config = require "groupbutler.config"
local null = require "groupbutler.null"
+local Chat = require("groupbutler.chat")
+local ChatMember = require("groupbutler.chatmember")
local _M = {}
@@ -55,54 +57,54 @@ function _M:on_message()
local red = self.red
local i18n = self.i18n
- if not msg.inline and msg.spam and msg.chat.id < 0 and not msg.cb and not msg:is_from_admin() then
- local status = red:hget('chat:'..msg.chat.id..':antispam', msg.spam)
+ if not msg.inline and msg.spam and msg.from.chat.id < 0 and not msg.cb and not msg.from:isAdmin() then
+ local status = red:hget('chat:'..msg.from.chat.id..':antispam', msg.spam)
if status ~= null and status ~= 'alwd' then
local whitelisted
if msg.spam == 'links' then
- whitelisted = is_whitelisted(self, msg.chat.id, msg.text:lower())
+ whitelisted = is_whitelisted(self, msg.from.chat.id, msg.text:lower())
--[[elseif msg.forward_from_chat then
if msg.forward_from_chat.type == 'channel' then
- whitelisted = is_whitelisted_channel(msg.chat.id, msg.forward_from_chat.id)
+ whitelisted = is_whitelisted_channel(msg.from.chat.id, msg.forward_from_chat.id)
end]]
end
if not whitelisted then
local hammer_text = nil
- local name = u:getname_final(msg.from)
- local warns_received, max_allowed = getAntispamWarns(self, msg.chat.id, msg.from.id) --also increases the warns counter
+ local name = msg.from.user:getLink()
+ local warns_received, max_allowed = getAntispamWarns(self, msg.from.chat.id, msg.from.user.id) --also increases the warns counter
if warns_received >= max_allowed then
if status == 'del' then
- api:deleteMessage(msg.chat.id, msg.message_id)
+ api:deleteMessage(msg.from.chat.id, msg.message_id)
end
- local action = red:hget('chat:'..msg.chat.id..':antispam', 'action')
+ local action = red:hget('chat:'..msg.from.chat.id..':antispam', 'action')
if action == null then action = config.chat_settings['antispam']['action'] end
local res
if action == 'ban' then
- res = u:banUser(msg.chat.id, msg.from.id)
+ res = msg.from:ban()
elseif action == 'kick' then
- res = u:kickUser(msg.chat.id, msg.from.id)
+ res = msg.from:kick()
elseif action == 'mute' then
- res = u:muteUser(msg.chat.id, msg.from.id)
+ res = msg.from:mute()
end
if res then
- red:hdel('chat:'..msg.chat.id..':spamwarns', msg.from.id) --remove spam warns
- api:sendMessage(msg.chat.id,
+ red:hdel('chat:'..msg.from.chat.id..':spamwarns', msg.from.user.id) --remove spam warns
+ api:sendMessage(msg.from.chat.id,
i18n('%s %s for spam! (%d/%d)'):format(name, humanizations(self)[action], warns_received, max_allowed),
'html'
)
end
else
if status == 'del' and warns_received == max_allowed - 1 then
- api:deleteMessage(msg.chat.id, msg.message_id)
+ api:deleteMessage(msg.from.chat.id, msg.message_id)
msg:send_reply(i18n('%s, spam is not allowed here. The next time you will be restricted'):format(name),
'html')
elseif status == 'del' then
--just delete
- api:deleteMessage(msg.chat.id, msg.message_id)
+ api:deleteMessage(msg.from.chat.id, msg.message_id)
elseif status ~= 'del' then
msg:send_reply(i18n("%s, this kind of message is not allowed in this chat (%d/%d)")
:format(name, warns_received, max_allowed), 'html')
@@ -279,44 +281,48 @@ end
function _M:onCallbackQuery(blocks)
local api = self.api
local msg = self.message
- local u = self.u
local i18n = self.i18n
if blocks[1] == 'alert' then
local text = get_alert_text(self, blocks[2])
api:answerCallbackQuery(msg.cb_id, text, true, config.bot_settings.cache_time.alert_help)
- else
+ return
+ end
- local chat_id = msg.target_id
- if not u:is_allowed('config', chat_id, msg.from) then
- api:answerCallbackQuery(msg.cb_id, i18n("You're no longer an admin"))
- else
- local antispam_first = i18n([[*Anti-spam settings*
+ local member = ChatMember:new({
+ chat = Chat:new({id=msg.target_id}, self),
+ user = msg.from.user,
+ }, self)
+
+ if not member:can("can_change_info") then
+ api:answerCallbackQuery(msg.cb_id, i18n("Sorry, you don't have permission to change settings"))
+ return
+ end
+
+ local antispam_first = i18n([[*Anti-spam settings*
Choose which kind of message you want to forbid
• ✅ = *Allowed*
• ❌ = *Not allowed*
• 🗑 = *Delete*
When set on `delete`, the bot doesn't warn users until they are about to be kicked/banned/muted (at the second-to-last warning)]]) -- luacheck: ignore 631
- local keyboard, text
-
- if blocks[1] == 'toggle' then
- if blocks[2] == 'forwards' or blocks[2] == 'links' then
- text = toggleAntispamSetting(self, chat_id, blocks[2])
- else
- if blocks[2] == 'raise' or blocks[2] == 'dim' then
- text = changeWarnsNumber(self, chat_id, blocks[2])
- elseif blocks[2] == 'action' then
- text = changeAction(self, chat_id, blocks[2])
- end
- end
- end
+ local keyboard, text
- keyboard = doKeyboard_antispam(self, chat_id)
- api:editMessageText(msg.chat.id, msg.message_id, nil, antispam_first, "Markdown", nil, keyboard)
- if text then api:answerCallbackQuery(msg.cb_id, text) end
+ if blocks[1] == "toggle" then
+ if blocks[2] == "forwards" or blocks[2] == "links" then
+ text = toggleAntispamSetting(self, member.chat.id, blocks[2])
+ elseif blocks[2] == "raise" or blocks[2] == "dim" then
+ text = changeWarnsNumber(self, member.chat.id, blocks[2])
+ elseif blocks[2] == "action" then
+ text = changeAction(self, member.chat.id, blocks[2])
end
end
+
+ keyboard = doKeyboard_antispam(self, member.chat.id)
+ api:editMessageText(msg.from.chat.id, msg.message_id, nil, antispam_first, "Markdown", nil, keyboard)
+ if text then
+ api:answerCallbackQuery(msg.cb_id, text)
+ end
end
local function edit_channels_whitelist(self, chat_id, list, action)
@@ -348,65 +354,36 @@ end
function _M:onTextMessage(blocks)
local msg = self.message
- local u = self.u
local red = self.red
local i18n = self.i18n
- if u:is_allowed('texts', msg.chat.id, msg.from) then
- if (blocks[1] == 'wl' or blocks[1] == 'whitelist') and blocks[2] then
- if blocks[2] == '-' then
- local set = ('chat:%d:whitelist'):format(msg.chat.id)
- local n = red:scard(set) or 0
- local text
- if n == 0 then
- text = i18n("_The whitelist was already empty_")
- else
- red:del(set)
- text = i18n("*Whitelist cleaned*\n%d links have been removed"):format(n)
- end
- msg:send_reply(text, "Markdown")
- else
- local text
- if msg.entities then
- local links = urls_table(msg.entities, msg.text)
- if not next(links) then
- text = i18n("_I can't find any url in this message_")
- else
- local new = red:sadd(('chat:%d:whitelist'):format(msg.chat.id), unpack(links))
- text = i18n("%d link(s) will be whitelisted"):format(#links - (#links - new))
- if new ~= #links then
- text = text..i18n("\n%d links were already in the list"):format(#links - new)
- end
- end
- else
- text = i18n("_I can't find any url in this message_")
- end
- msg:send_reply(text, "Markdown")
- end
- end
- if (blocks[1] == 'wl' or blocks[1] == 'whitelist') and not blocks[2] then
- local links = red:smembers(('chat:%d:whitelist'):format(msg.chat.id))
- if not next(links) then
- msg:send_reply(i18n("_The whitelist is empty_.\nUse `/wl [links]` to add some links to the whitelist"),"Markdown")
+ if not msg.from:isAdmin() then
+ return
+ end
+
+ if (blocks[1] == 'wl' or blocks[1] == 'whitelist') and blocks[2] then
+ if blocks[2] == '-' then
+ local set = ('chat:%d:whitelist'):format(msg.from.chat.id)
+ local n = red:scard(set) or 0
+ local text
+ if n == 0 then
+ text = i18n("_The whitelist was already empty_")
else
- local text = i18n("Whitelisted links:\n\n")
- for i=1, #links do
- text = text..'• '..links[i]..'\n'
- end
- msg:send_reply(text)
+ red:del(set)
+ text = i18n("*Whitelist cleaned*\n%d links have been removed"):format(n)
end
- end
- if blocks[1] == 'unwl' or blocks[1] == 'unwhitelist' then
+ msg:send_reply(text, "Markdown")
+ else
local text
if msg.entities then
local links = urls_table(msg.entities, msg.text)
if not next(links) then
text = i18n("_I can't find any url in this message_")
else
- local removed = red:srem(('chat:%d:whitelist'):format(msg.chat.id), unpack(links))
- text = i18n("%d link(s) removed from the whitelist"):format(removed)
- if removed ~= #links then
- text = text..i18n("\n%d links were already in the list"):format(#links - removed)
+ local new = red:sadd(('chat:%d:whitelist'):format(msg.from.chat.id), unpack(links))
+ text = i18n("%d link(s) will be whitelisted"):format(#links - (#links - new))
+ if new ~= #links then
+ text = text..i18n("\n%d links were already in the list"):format(#links - new)
end
end
else
@@ -414,52 +391,91 @@ function _M:onTextMessage(blocks)
end
msg:send_reply(text, "Markdown")
end
- if blocks[1] == 'funwl' then --force the unwhitelist of a link
- red:srem(('chat:%d:whitelist'):format(msg.chat.id), blocks[2])
- msg:send_reply('Done')
- end
- if blocks[1] == 'wlchan' and not blocks[2] then
- local channels = red:smembers(('chat:%d:chanwhitelist'):format(msg.chat.id))
- if not next(channels) then
- msg:send_reply(i18n("_Whitelist of channels empty_"), "Markdown")
- else
- msg:send_reply(i18n("*Whitelisted channels:*\n%s"):format(table.concat(channels, '\n')), "Markdown")
+ return
+ end
+
+ if (blocks[1] == 'wl' or blocks[1] == 'whitelist') and not blocks[2] then
+ local links = red:smembers(('chat:%d:whitelist'):format(msg.from.chat.id))
+ if not next(links) then
+ msg:send_reply(i18n("_The whitelist is empty_.\nUse `/wl [links]` to add some links to the whitelist"),"Markdown")
+ else
+ local text = i18n("Whitelisted links:\n\n")
+ for i=1, #links do
+ text = text..'• '..links[i]..'\n'
end
+ msg:send_reply(text)
end
- if blocks[1] == 'wlchan' and blocks[2] then
- local for_entered, channels = edit_channels_whitelist(self, msg.chat.id, blocks[2], 'add')
+ return
+ end
- if not for_entered then
- msg:send_reply(i18n("_I can't find a channel ID in your message_"), "Markdown")
+ if blocks[1] == 'unwl' or blocks[1] == 'unwhitelist' then
+ local text
+ if msg.entities then
+ local links = urls_table(msg.entities, msg.text)
+ if not next(links) then
+ text = i18n("_I can't find any url in this message_")
else
- local text = ''
- if next(channels.valid) then
- text = text..("*Channels whitelisted*: `%s`\n"):format(table.concat(channels.valid, ', '))
- end
- if next(channels.not_valid) then
- text = text..("*Channels already whitelisted*: `%s`\n"):format(table.concat(channels.not_valid, ', '))
+ local removed = red:srem(('chat:%d:whitelist'):format(msg.from.chat.id), unpack(links))
+ text = i18n("%d link(s) removed from the whitelist"):format(removed)
+ if removed ~= #links then
+ text = text..i18n("\n%d links were already in the list"):format(#links - removed)
end
-
- msg:send_reply(text, "Markdown")
end
+ else
+ text = i18n("_I can't find any url in this message_")
end
- if blocks[1] == 'unwlchan' then
- local for_entered, channels = edit_channels_whitelist(self, msg.chat.id, blocks[2], 'rem')
+ msg:send_reply(text, "Markdown")
+ return
+ end
- if not for_entered then
- msg:send_reply(i18n("_I can't find a channel ID in your message_"), "Markdown")
- else
- local text = ''
- if next(channels.valid) then
- text = text..("*Channels unwhitelisted*: `%s`\n"):format(table.concat(channels.valid, ', '))
- end
- if next(channels.not_valid) then
- text = text..("*Channels not whitelisted*: `%s`\n"):format(table.concat(channels.not_valid, ', '))
- end
+ if blocks[1] == 'funwl' then --force the unwhitelist of a link
+ red:srem(('chat:%d:whitelist'):format(msg.from.chat.id), blocks[2])
+ msg:send_reply('Done')
+ return
+ end
+
+ if blocks[1] == 'wlchan' and not blocks[2] then
+ local channels = red:smembers(('chat:%d:chanwhitelist'):format(msg.from.chat.id))
+ if not next(channels) then
+ msg:send_reply(i18n("_Whitelist of channels empty_"), "Markdown")
+ else
+ msg:send_reply(i18n("*Whitelisted channels:*\n%s"):format(table.concat(channels, '\n')), "Markdown")
+ end
+ return
+ end
- msg:send_reply(text, "Markdown")
+ if blocks[1] == 'wlchan' and blocks[2] then
+ local for_entered, channels = edit_channels_whitelist(self, msg.from.chat.id, blocks[2], 'add')
+ if not for_entered then
+ msg:send_reply(i18n("_I can't find a channel ID in your message_"), "Markdown")
+ else
+ local text = ''
+ if next(channels.valid) then
+ text = text..("*Channels whitelisted*: `%s`\n"):format(table.concat(channels.valid, ', '))
end
+ if next(channels.not_valid) then
+ text = text..("*Channels already whitelisted*: `%s`\n"):format(table.concat(channels.not_valid, ', '))
+ end
+ msg:send_reply(text, "Markdown")
+ end
+ return
+ end
+
+ if blocks[1] == 'unwlchan' then
+ local for_entered, channels = edit_channels_whitelist(self, msg.from.chat.id, blocks[2], 'rem')
+ if not for_entered then
+ msg:send_reply(i18n("_I can't find a channel ID in your message_"), "Markdown")
+ else
+ local text = ''
+ if next(channels.valid) then
+ text = text..("*Channels unwhitelisted*: `%s`\n"):format(table.concat(channels.valid, ', '))
+ end
+ if next(channels.not_valid) then
+ text = text..("*Channels not whitelisted*: `%s`\n"):format(table.concat(channels.not_valid, ', '))
+ end
+ msg:send_reply(text, "Markdown")
end
+ return
end
end
diff --git a/lua/groupbutler/plugins/backup.lua b/lua/groupbutler/plugins/backup.lua
index f7f2a0972..01576cbf1 100644
--- a/lua/groupbutler/plugins/backup.lua
+++ b/lua/groupbutler/plugins/backup.lua
@@ -89,10 +89,10 @@ function _M:onTextMessage(blocks)
local i18n = self.i18n
local u = self.u
- if not msg:is_from_admin() then return end
+ if not msg.from:isAdmin() then return end
if blocks[1] == 'snap' then
- local key = 'chat:'..msg.chat.id..':lastsnap'
+ local key = 'chat:'..msg.from.chat.id..':lastsnap'
local last_user = red:get(key)
if last_user ~= null then -- A snapshot has been done recently
local ttl = red:ttl(key)
@@ -104,12 +104,12 @@ Wait [%s
] to use it again
return
end
- local file_path = gen_backup(self, msg.chat.id)
- local ok = api:sendDocument(msg.from.id, {path = file_path}, ('#snap\n%s'):format(msg.chat.title))
+ local file_path = gen_backup(self, msg.from.chat.id)
+ local ok = api:sendDocument(msg.from.user.id, {path = file_path}, ('#snap\n%s'):format(msg.from.chat.title))
if not ok then return end
- local name = u:getname_final(msg.from)
+ local name = msg.from.user:getLink()
red:setex(key, 10800, name) --3 hours
msg:send_reply(i18n('*Sent in private*'), "Markdown")
return
@@ -118,36 +118,36 @@ Wait [%s
] to use it again
local text
if not msg.reply then
text = i18n('Invalid input. Please reply to the backup file (/snap command to get it)')
- api:sendMessage(msg.chat.id, text)
+ api:sendMessage(msg.from.chat.id, text)
return
end
if not msg.reply.document then
text = i18n('Invalid input. Please reply to a document')
- api:sendMessage(msg.chat.id, text)
+ api:sendMessage(msg.from.chat.id, text)
return
end
- if msg.reply.document.file_name ~= 'snap'..msg.chat.id..'.gbb' then
+ if msg.reply.document.file_name ~= 'snap'..msg.from.chat.id..'.gbb' then
text = i18n('This is not a valid backup file.\nReason: invalid name (%s)')
:format(tostring(msg.reply_to_message.document.file_name))
- api:sendMessage(msg.chat.id, text)
+ api:sendMessage(msg.from.chat.id, text)
return
end
local res = api:getFile(msg.reply.document.file_id)
local download_link = u:telegram_file_link(res)
- local file_path, code = u:download_to_file(download_link, '/tmp/'..msg.chat.id..'.json')
+ local file_path, code = u:download_to_file(download_link, '/tmp/'..msg.from.chat.id..'.json')
if not file_path then
text = i18n('Download of the file failed with code %s'):format(tostring(code))
- api:sendMessage(msg.chat.id, text)
+ api:sendMessage(msg.from.chat.id, text)
return
end
local data = load_data(file_path)
for chat_id, group_data in pairs(data) do
chat_id = tonumber(chat_id)
- if tonumber(chat_id) ~= msg.chat.id then
- text = i18n('Chat IDs don\'t match (%s and %s)'):format(tostring(chat_id), tostring(msg.chat.id))
- api:sendMessage(msg.chat.id, text)
+ if tonumber(chat_id) ~= msg.from.chat.id then
+ text = i18n('Chat IDs don\'t match (%s and %s)'):format(tostring(chat_id), tostring(msg.from.chat.id))
+ api:sendMessage(msg.from.chat.id, text)
return
end
--restoring sets
@@ -169,7 +169,7 @@ Wait [%s
] to use it again
end
end
end
- api:sendMessage(msg.chat.id, i18n([[Import was successful.
+ api:sendMessage(msg.from.chat.id, i18n([[Import was successful.
Important:
- #extra commands which are associated with a media must be set again if the bot you are using now is different from the bot that originated the backup.
diff --git a/lua/groupbutler/plugins/banhammer.lua b/lua/groupbutler/plugins/banhammer.lua
index afdacf836..224af23f7 100644
--- a/lua/groupbutler/plugins/banhammer.lua
+++ b/lua/groupbutler/plugins/banhammer.lua
@@ -1,4 +1,6 @@
local config = require "groupbutler.config"
+local ChatMember = require("groupbutler.chatmember")
+local User = require("groupbutler.user")
local _M = {}
@@ -57,21 +59,21 @@ function _M:onTextMessage(blocks)
local api_err = self.api_err
local u = self.u
- if msg.chat.type == "private"
- or not u:can(msg.chat.id, msg.from.id, "can_restrict_members") then
+ if msg.from.chat.type == "private"
+ or not msg.from:can("can_restrict_members") then
return
end
-
- local user_id, error_translation_key = u:get_user_id(msg, blocks)
-
- if not user_id and blocks[1] ~= "kickme" and blocks[1] ~= "fwdban" then
- msg:send_reply(error_translation_key, "Markdown")
- return
+ local admin = msg.from
+ local target
+ do
+ local err
+ target, err = msg:getTargetMember(blocks)
+ if not target and blocks[1] ~= "fwdban" then
+ msg:send_reply(err, "Markdown")
+ return
+ end
end
- if tonumber(user_id) == bot.id then return end
-
- local chat_id = msg.chat.id
- local admin, kicked = u:getnames_complete(msg, blocks)
+ if tonumber(target.user.id) == bot.id then return end
--print(get_motivation(msg))
@@ -81,63 +83,87 @@ function _M:onTextMessage(blocks)
if tonumber(time_value) > 100 then
time_value = 100
end
- local key = ('chat:%s:%s:tbanvalue'):format(msg.chat.id, user_id)
+ local key = ('chat:%s:%s:tbanvalue'):format(msg.from.chat.id, target.user.id)
red:setex(key, 3600, time_value)
end
-
- local markup = markup_tempban(self, msg.chat.id, user_id)
+ local markup = markup_tempban(self, msg.from.chat.id, target.user.id)
msg:send_reply(i18n("Use -/+ to edit the value, then select a timeframe to temporary ban the user"),
"Markdown", nil, nil, markup)
end
+
+ local text
if blocks[1] == 'kick' then
- local ok, err = u:kickUser(chat_id, user_id)
- if ok then
- u:logEvent('kick', msg, {motivation = get_motivation(msg), admin = admin, user = kicked, user_id = user_id})
- api:sendMessage(msg.chat.id, i18n("%s kicked %s!"):format(admin, kicked), 'html', true)
- else
+ local ok, err = target:kick()
+ if not ok then
msg:send_reply(err, "Markdown")
+ return
end
+ u:logEvent("kick", msg, {
+ motivation = get_motivation(msg),
+ admin = admin.user,
+ user = target.user,
+ user_id = target.user.id
+ })
+ text = i18n("%s kicked %s!"):format(admin.user:getLink(), target.user:getLink())
+ api:sendMessage(msg.from.chat.id, text, "html", true)
end
+
if blocks[1] == 'ban' then
- local ok, err = u:banUser(chat_id, user_id)
- if ok then
- u:logEvent('ban', msg, {motivation = get_motivation(msg), admin = admin, user = kicked, user_id = user_id})
- api:sendMessage(msg.chat.id, i18n("%s banned %s!"):format(admin, kicked), 'html', true)
- else
+ local ok, err = target:ban()
+ if not ok then
msg:send_reply(err, "Markdown")
end
+ u:logEvent("ban", msg, {
+ motivation = get_motivation(msg),
+ admin = admin.user,
+ user = target.user,
+ user_id = target.user.id
+ })
+ text = i18n("%s banned %s!"):format(admin.user:getLink(), target.user:getLink())
+ api:sendMessage(msg.from.chat.id, text, "html", true)
end
+
if blocks[1] == 'fwdban' then
if not msg.reply or not msg.reply.forward_from then
msg:send_reply(i18n("_Use this command in reply to a forwarded message_"), "Markdown")
- else
- user_id = msg.reply.forward_from.id
- local ok, err = u:banUser(chat_id, user_id)
- if ok then
- u:logEvent('ban', msg, {motivation = get_motivation(msg), admin = admin, user = kicked, user_id = user_id})
- api:sendMessage(msg.chat.id, i18n("%s banned %s!"):format(admin, u:getname_final(msg.reply.forward_from)), 'html')
- else
- msg:send_reply(err, "Markdown")
- end
+ return
end
+ target = msg.reply.forward_from
+ local ok, err = target:ban()
+ if not ok then
+ msg:send_reply(err, "Markdown")
+ end
+ u:logEvent("ban", msg, {
+ motivation = get_motivation(msg),
+ admin = admin.user,
+ user = target.user,
+ user_id = target.user.id
+ })
+ text = i18n("%s banned %s!"):format(admin.user:getLink(), target.user:getLink())
+ api:sendMessage(msg.from.chat.id, text, "html", true)
end
+
if blocks[1] == 'unban' then
- if u:is_admin(chat_id, user_id) then
+ if target:isAdmin() then
msg:send_reply(i18n("_An admin can't be unbanned_"), "Markdown")
return
end
- local ok, err = api:getChatMember(chat_id, user_id)
- if not ok then
- msg:send_reply(api_err:trans(err), 'html')
+ if target.status ~= "kicked" then
+ msg:send_reply(i18n("This user is not banned!"))
return
end
- if ok.status ~= 'kicked' then
- msg:send_reply(i18n("This user is not banned!"))
+ local ok, err = api:unbanChatMember(target.chat.id, target.user.id)
+ if not ok then
+ msg:send_reply(api_err:trans(err), "Markdown")
return
end
- api:unbanChatMember(chat_id, user_id)
- u:logEvent('unban', msg, {motivation = get_motivation(msg), admin = admin, user = kicked, user_id = user_id})
- msg:send_reply(i18n("%s unbanned by %s!"):format(kicked, admin), 'html')
+ u:logEvent("unban", msg, {
+ motivation = get_motivation(msg),
+ admin = admin,
+ user = target.user,
+ user_id = target.user.id
+ })
+ msg:send_reply(i18n("%s unbanned by %s!"):format(target.user:getLink(), admin.user:getLink()), 'html')
end
end
@@ -146,67 +172,75 @@ function _M:onCallbackQuery(matches)
local msg = self.message
local red = self.red
local i18n = self.i18n
- local u = self.u
- if not u:can(msg.chat.id, msg.from.id, 'can_restrict_members') then
+ if msg.from.chat.type == "private"
+ or not msg.from:can("can_restrict_members") then
api:answerCallbackQuery(msg.cb_id, i18n("Sorry, you don't have permission to restrict members"), true)
- else
- if matches[1] == 'nil' then
- api:answerCallbackQuery(msg.cb_id,
- i18n("Tap on the -/+ buttons to change this value. Then select a timeframe to execute the ban"), true)
- elseif matches[1] == 'val' then
- local user_id = matches[3]
- local key = ('chat:%d:%s:tbanvalue'):format(msg.chat.id, user_id)
- local current_value, new_value
- current_value = tonumber(red:get(key)) or 3
- if matches[2] == 'm' then
- new_value = current_value - 1
- if new_value < 1 then
- api:answerCallbackQuery(msg.cb_id, i18n("You can't set a lower value"))
- return --don't proceed
- else
- red:setex(key, 3600, new_value)
- end
- elseif matches[2] == 'p' then
- new_value = current_value + 1
- if new_value > 100 then
- api:answerCallbackQuery(msg.cb_id, i18n("Stop!!!"), true)
- return --don't proceed
- else
- red:setex(key, 3600, new_value)
- end
- end
+ return
+ end
- local markup = markup_tempban(self, msg.chat.id, user_id, new_value)
- api:editMessageReplyMarkup(msg.chat.id, msg.message_id, nil, markup)
- elseif matches[1] == 'ban' then
- local user_id = matches[3]
- local key = ('chat:%d:%s:tbanvalue'):format(msg.chat.id, user_id)
- local time_value = tonumber(red:get(key)) or 3
- local timeframe_string, until_date
- if matches[2] == 'h' then
- time_value = time_value <= 24 and time_value or 24
- timeframe_string = i18n('hours')
- until_date = msg.date + (time_value * 3600)
- elseif matches[2] == 'd' then
- time_value = time_value <= 30 and time_value or 30
- timeframe_string = i18n('days')
- until_date = msg.date + (time_value * 3600 * 24)
- elseif matches[2] == 'm' then
- time_value = time_value <= 60 and time_value or 60
- timeframe_string = i18n('minutes')
- until_date = msg.date + (time_value * 60)
- end
+ if matches[1] == "nil" then
+ api:answerCallbackQuery(msg.cb_id,
+ i18n("Tap on the -/+ buttons to change this value. Then select a timeframe to execute the ban"), true)
+ return
+ end
- local ok, err = u:banUser(msg.chat.id, user_id, until_date)
- if ok then
- local text = i18n("User banned for %d %s"):format(time_value, timeframe_string)
- api:editMessageText(msg.chat.id, msg.message_id, nil, text)
- red:del(key)
+ local target = ChatMember:new({
+ user = User:new({id=matches[3]}, self),
+ chat = msg.from.chat,
+ })
+
+ if matches[1] == "val" then
+ local key = ("chat:%d:%s:tbanvalue"):format(msg.from.chat.id, target.user.id)
+ local current_value, new_value
+ current_value = tonumber(red:get(key)) or 3
+ if matches[2] == "m" then
+ new_value = current_value - 1
+ if new_value < 1 then
+ api:answerCallbackQuery(msg.cb_id, i18n("You can't set a lower value"))
+ return --don't proceed
+ else
+ red:setex(key, 3600, new_value)
+ end
+ elseif matches[2] == "p" then
+ new_value = current_value + 1
+ if new_value > 100 then
+ api:answerCallbackQuery(msg.cb_id, i18n("Stop!!!"), true)
+ return --don't proceed
else
- api:editMessageText(msg.chat.id, msg.message_id, nil, err)
+ red:setex(key, 3600, new_value)
end
end
+ local markup = markup_tempban(self, msg.from.chat.id, target.user.id, new_value)
+ api:editMessageReplyMarkup(msg.from.chat.id, msg.message_id, nil, markup)
+ return
+ end
+
+ if matches[1] == 'ban' then
+ local key = ('chat:%d:%s:tbanvalue'):format(msg.from.chat.id, target.user.id)
+ local time_value = tonumber(red:get(key)) or 3
+ local timeframe_string, until_date
+ if matches[2] == 'h' then
+ time_value = time_value <= 24 and time_value or 24
+ timeframe_string = i18n('hours')
+ until_date = msg.date + (time_value * 3600)
+ elseif matches[2] == 'd' then
+ time_value = time_value <= 30 and time_value or 30
+ timeframe_string = i18n('days')
+ until_date = msg.date + (time_value * 3600 * 24)
+ elseif matches[2] == 'm' then
+ time_value = time_value <= 60 and time_value or 60
+ timeframe_string = i18n('minutes')
+ until_date = msg.date + (time_value * 60)
+ end
+ local ok, err = target:ban(until_date)
+ if not ok then
+ api:editMessageText(msg.from.chat.id, msg.message_id, nil, err)
+ return
+ end
+ local text = i18n("User banned for %d %s"):format(time_value, timeframe_string)
+ api:editMessageText(msg.from.chat.id, msg.message_id, nil, text)
+ red:del(key)
end
end
diff --git a/lua/groupbutler/plugins/configure.lua b/lua/groupbutler/plugins/configure.lua
index 359c6c2e6..e154b8969 100644
--- a/lua/groupbutler/plugins/configure.lua
+++ b/lua/groupbutler/plugins/configure.lua
@@ -1,5 +1,7 @@
local config = require "groupbutler.config"
local api_u = require "telegram-bot-api.utilities"
+local Chat = require("groupbutler.chat")
+local ChatMember = require("groupbutler.chatmember")
local _M = {}
@@ -12,42 +14,48 @@ function _M:new(update_obj)
return plugin_obj
end
-local function doKeyboardConfig(self, chat_id, user_id)
- local u = self.u
+local function doKeyboardConfig(self, member)
local i18n = self.i18n
local reply_markup = api_u.InlineKeyboardMarkup:new()
- reply_markup:row({text = i18n("🛠 Menu"), callback_data = 'config:menu:'..chat_id})
- reply_markup:row({text = i18n("⚡️ Antiflood"), callback_data = 'config:antiflood:'..chat_id})
- reply_markup:row({text = i18n("🌈 Media"), callback_data = 'config:media:'..chat_id})
- reply_markup:row({text = i18n("🚫 Antispam"), callback_data = 'config:antispam:'..chat_id})
- reply_markup:row({text = i18n("📥 Log channel"), callback_data = 'config:logchannel:'..chat_id})
-
- if u:can(chat_id, user_id, "can_restrict_members") then
- reply_markup:row({text = i18n("⛔️ Default permissions"), callback_data = 'config:defpermissions:'..chat_id})
+ reply_markup:row({text = i18n("🛠 Menu"), callback_data = "config:menu:"..member.chat.id})
+ reply_markup:row({text = i18n("⚡️ Antiflood"), callback_data = "config:antiflood:"..member.chat.id})
+ reply_markup:row({text = i18n("🌈 Media"), callback_data = "config:media:"..member.chat.id})
+ reply_markup:row({text = i18n("🚫 Antispam"), callback_data = "config:antispam:"..member.chat.id})
+ reply_markup:row({text = i18n("📥 Log channel"), callback_data = "config:logchannel:"..member.chat.id})
+ if member:can("can_restrict_members") then
+ reply_markup:row({text = i18n("⛔️ Default permissions"), callback_data = "config:defpermissions:"..member.chat.id}) -- luacheck: ignore 631
end
return reply_markup
end
+local function messageConstructor(self, member)
+ local i18n = self.i18n
+ local text = i18n("Change the settings of your group")
+ if member.chat.title then
+ text = ("%s\n"):format(member.chat.title:escape_html())..text
+ end
+ return {
+ chat_id = member.user.id,
+ text = text,
+ parse_mode = "html",
+ reply_markup = doKeyboardConfig(self, member)
+ }
+end
+
function _M:onTextMessage()
local api = self.api
local msg = self.message
local u = self.u
local i18n = self.i18n
- if msg.chat.type ~= "supergroup"
- or not msg:is_from_admin() then
+ if msg.from.chat.type ~= "supergroup"
+ or not msg.from:isAdmin() then
return
end
- msg.chat:cache()
- local res = api:sendMessage({
- chat_id = msg.from.id,
- text = ("%s\n"):format(msg.chat.title:escape_html())..i18n("Change the settings of your group"),
- parse_mode = "html",
- reply_markup = doKeyboardConfig(self, msg.chat.id, msg.from.id),
- })
+ local res = api:sendMessage(messageConstructor(self, msg.from))
- if u:is_silentmode_on(msg.chat.id) then -- send the response in the group only if the silent mode is off
+ if u:is_silentmode_on(msg.from.chat.id) then -- send the response in the group only if the silent mode is off
return
end
@@ -55,29 +63,21 @@ function _M:onTextMessage()
u:sendStartMe(msg)
return
end
- api:sendMessage(msg.chat.id, i18n("_I've sent you the keyboard via private message_"), "Markdown")
+ api:sendMessage(msg.from.chat.id, i18n("_I've sent you the keyboard via private message_"), "Markdown")
end
function _M:onCallbackQuery()
local api = self.api
local msg = self.message
- local i18n = self.i18n
- local db = self.db
- local chat_id = msg.target_id
- local text = i18n("Change the settings of your group")
- local chat_title = db:getChatTitle({id=chat_id})
- if chat_title then
- text = ("%s\n"):format(chat_title:escape_html())..text
- end
+ local member = ChatMember:new({
+ chat = Chat:new({id=msg.target_id}, self),
+ user = msg.from.user,
+ }, self)
- api:editMessageText({
- chat_id = msg.chat.id,
- message_id = msg.message_id,
- text = text,
- parse_mode = "html",
- reply_markup = doKeyboardConfig(self, chat_id, msg.from.id)
- })
+ local body = messageConstructor(self, member)
+ body.message_id = msg.message_id
+ api:editMessageText(body)
end
_M.triggers = {
diff --git a/lua/groupbutler/plugins/dashboard.lua b/lua/groupbutler/plugins/dashboard.lua
index 1ac36d870..0ef5e37a8 100644
--- a/lua/groupbutler/plugins/dashboard.lua
+++ b/lua/groupbutler/plugins/dashboard.lua
@@ -1,5 +1,7 @@
local config = require "groupbutler.config"
local null = require "groupbutler.null"
+local Chat = require("groupbutler.chat")
+local ChatMember = require("groupbutler.chatmember")
local _M = {}
@@ -85,21 +87,21 @@ function _M:onTextMessage()
local msg = self.message
local u = self.u
local i18n = self.i18n
- if msg.chat.type ~= 'private' then
- local chat_id = msg.chat.id
+ if msg.from.chat.type ~= 'private' then
+ local chat_id = msg.from.chat.id
local reply_markup = doKeyboard_dashboard(self, chat_id)
local ok = api:send_message{
- chat_id = msg.from.id,
+ chat_id = msg.from.user.id,
text = i18n("Navigate this message to see *all the info* about this group!"),
parse_mode = "Markdown",
reply_markup = reply_markup
}
- if not u:is_silentmode_on(msg.chat.id) then --send the responde in the group only if the silent mode is off
+ if not u:is_silentmode_on(msg.from.chat.id) then --send the responde in the group only if the silent mode is off
if not ok then
u:sendStartMe(msg)
return
end
- api:sendMessage(msg.chat.id, i18n("_I've sent you the group dashboard via private message_"), "Markdown")
+ api:sendMessage(msg.from.chat.id, i18n("_I've sent you the group dashboard via private message_"), "Markdown")
end
end
end
@@ -110,35 +112,35 @@ function _M:onCallbackQuery(blocks)
local u = self.u
local red = self.red
local i18n = self.i18n
- local chat_id = msg.target_id
+
local request = blocks[2]
local text, notification
local parse_mode = "Markdown"
- local res = api:getChat(chat_id)
- if not res then
- api:answerCallbackQuery(msg.cb_id, i18n("🚫 This group does not exist"))
- return
- end
- -- Private chats don't have a username
- local private = not res.username
- res = api:getChatMember(chat_id, msg.from.id)
- if not res or (res.status == 'left' or res.status == 'kicked') and private then
- api:editMessageText(msg.from.id, msg.message_id, nil, i18n("🚷 You are not a member of the chat. " ..
+
+ local chat = Chat:new({id=msg.target_id}, self)
+ local member = ChatMember:new({
+ chat = chat,
+ user = msg.from.user,
+ }, self)
+
+ if member.status == 'left'
+ or member.status == 'kicked' then
+ api:editMessageText(msg.from.user.id, msg.message_id, nil, i18n("🚷 You are not a member of the chat. " ..
"You can't see the settings of a private group."))
return
end
- local reply_markup = doKeyboard_dashboard(self, chat_id)
+ local reply_markup = doKeyboard_dashboard(self, chat.id)
if request == 'settings' then
- text = u:getSettings(chat_id)
+ text = u:getSettings(chat.id)
notification = i18n("ℹ️ Group ► Settings")
end
if request == 'rules' then
- text = u:getRules(chat_id)
+ text = u:getRules(chat.id)
notification = i18n("ℹ️ Group ► Rules")
end
if request == 'adminlist' then
parse_mode = 'html'
- local adminlist = u:getAdminlist(chat_id)
+ local adminlist = u:getAdminlist(chat)
if adminlist then
text = adminlist
else
@@ -147,11 +149,11 @@ function _M:onCallbackQuery(blocks)
notification = i18n("ℹ️ Group ► Admin list")
end
if request == 'extra' then
- text = u:getExtraList(chat_id)
+ text = u:getExtraList(chat.id)
notification = i18n("ℹ️ Group ► Extra")
end
if request == 'flood' then
- text = getFloodSettings_text(self, chat_id)
+ text = getFloodSettings_text(self, chat.id)
notification = i18n("ℹ️ Group ► Flood")
end
if request == 'media' then
@@ -172,7 +174,7 @@ function _M:onCallbackQuery(blocks)
}
text = i18n("*Current media settings*:\n\n")
for media, default_status in pairs(config.chat_settings['media']) do
- local status = red:hget('chat:'..chat_id..':media', media)
+ local status = red:hget('chat:'..chat.id..':media', media)
if status == null then status = default_status end
if status == 'ok' then
status = '✅'
@@ -184,7 +186,7 @@ function _M:onCallbackQuery(blocks)
end
notification = i18n("ℹ️ Group ► Media")
end
- api:edit_message_text(msg.from.id, msg.message_id, nil, text, parse_mode, true, reply_markup)
+ api:edit_message_text(msg.from.user.id, msg.message_id, nil, text, parse_mode, true, reply_markup)
api:answerCallbackQuery(msg.cb_id, notification)
end
diff --git a/lua/groupbutler/plugins/defaultpermissions.lua b/lua/groupbutler/plugins/defaultpermissions.lua
index 001983a71..d10db306e 100644
--- a/lua/groupbutler/plugins/defaultpermissions.lua
+++ b/lua/groupbutler/plugins/defaultpermissions.lua
@@ -1,5 +1,7 @@
local config = require "groupbutler.config"
local null = require "groupbutler.null"
+local Chat = require("groupbutler.chat")
+local ChatMember = require("groupbutler.chatmember")
local _M = {}
@@ -116,44 +118,52 @@ end
function _M:onCallbackQuery(blocks)
local api = self.api
local msg = self.message
- local u = self.u
local i18n = self.i18n
+
if blocks[1] == 'alert' then
local text = get_alert_text(self, blocks[2])
api:answerCallbackQuery(msg.cb_id, text, true, config.bot_settings.cache_time.alert_help)
- else
- local chat_id = msg.target_id
- if not u:can(chat_id, msg.from.id, 'can_restrict_members') then
- api:answerCallbackQuery(msg.cb_id, i18n("Sorry, you don't have permission to restrict members"))
- else
- local msg_text = i18n([[*Default permissions*
+ return
+ end
+
+ local member = ChatMember:new({
+ chat = Chat:new({id=msg.target_id}, self),
+ user = msg.from.user,
+ }, self)
+
+ if not member:can("can_restrict_members") then
+ api:answerCallbackQuery(msg.cb_id, i18n("Sorry, you don't have permission to restrict members"))
+ return
+ end
+
+ local msg_text = i18n([[*Default permissions*
From this menu you can change the default permissions that will be granted when a new member join.
_Only the administrators with the permission to restrict a member can access this menu._
Tap on the name of a permission for a description of what kind of messages it will influence.
]])
- local reply_markup, popup_text, show_alert
-
- if blocks[1] == 'toggle' then
- popup_text = toggle_permissions_setting(self, chat_id, blocks[2])
- end
-
- reply_markup = doKeyboard_permissions(self, chat_id)
- local ok, err
- if blocks[2] then
- --if the user tapped on a keybord button, just edit the markup and not the whole message
- ok, err = api:editMessageReplyMarkup(msg.chat.id, msg.message_id, nil, reply_markup)
- else
- ok, err = api:editMessageText(msg.chat.id, msg.message_id, nil, msg_text, "Markdown", nil, reply_markup)
- end
-
- if not ok and err.retry_after then
- popup_text = i18n("Setting saved, but I can't edit the buttons because you are too fast! Wait other %d seconds")
- :format(err.retry_after)
- show_alert = true
- end
- if popup_text then api:answerCallbackQuery(msg.cb_id, popup_text, show_alert) end
- end
+ local reply_markup, popup_text, show_alert
+
+ if blocks[1] == 'toggle' then
+ popup_text = toggle_permissions_setting(self, member.chat.id, blocks[2])
+ end
+
+ reply_markup = doKeyboard_permissions(self, member.chat.id)
+ local ok, err
+ if blocks[2] then
+ --if the user tapped on a keybord button, just edit the markup and not the whole message
+ ok, err = api:editMessageReplyMarkup(msg.from.chat.id, msg.message_id, nil, reply_markup)
+ else
+ ok, err = api:editMessageText(msg.from.chat.id, msg.message_id, nil, msg_text, "Markdown", nil, reply_markup)
+ end
+
+ if not ok and err.retry_after then
+ popup_text = i18n("Setting saved, but I can't edit the buttons because you are too fast! Wait other %d seconds")
+ :format(err.retry_after)
+ show_alert = true
+ end
+ if popup_text then
+ api:answerCallbackQuery(msg.cb_id, popup_text, show_alert)
end
end
diff --git a/lua/groupbutler/plugins/extra.lua b/lua/groupbutler/plugins/extra.lua
index 07b4bf6b7..9fa9a96d1 100644
--- a/lua/groupbutler/plugins/extra.lua
+++ b/lua/groupbutler/plugins/extra.lua
@@ -57,12 +57,12 @@ function _M:onTextMessage(blocks)
local red = self.red
local i18n = self.i18n
local api_err = self.api_err
- if msg.chat.type == 'private' and not(blocks[1] == 'start') then return end
+ if msg.from.chat.type == 'private' and not(blocks[1] == 'start') then return end
if blocks[1] == 'extra' then
if not blocks[2] then return end
if not blocks[3] and not msg.reply then return end
- if not msg:is_from_admin() then return end
+ if not msg.from:isAdmin() then return end
if msg.reply and not blocks[3] then
local file_id = msg.reply:get_file_id()
@@ -77,34 +77,34 @@ function _M:onTextMessage(blocks)
to_save = '###file_id!'..v..'###:'..file_id
end
end
- red:hset('chat:'..msg.chat.id..':extra', blocks[2], to_save)
+ red:hset('chat:'..msg.from.chat.id..':extra', blocks[2], to_save)
msg:send_reply(i18n("This media has been saved as a response to %s"):format(blocks[2]))
else
- local hash = 'chat:'..msg.chat.id..':extra'
+ local hash = 'chat:'..msg.from.chat.id..':extra'
local new_extra = blocks[3]
local reply_markup, test_text = u:reply_markup_from_text(u:replaceholders(new_extra, msg))
test_text = test_text:gsub('\n', '')
local ok, err = msg:send_reply(test_text, "Markdown", reply_markup)
if not ok then
- api:sendMessage(msg.chat.id, api_err:trans(err), "Markdown")
+ api:sendMessage(msg.from.chat.id, api_err:trans(err), "Markdown")
return
end
red:hset(hash, blocks[2]:lower(), new_extra)
local msg_id = ok.message_id
- api:editMessageText(msg.chat.id, msg_id, nil, i18n("Command '%s' saved!"):format(blocks[2]))
+ api:editMessageText(msg.from.chat.id, msg_id, nil, i18n("Command '%s' saved!"):format(blocks[2]))
end
elseif blocks[1] == 'extra list' then
- local text = u:getExtraList(msg.chat.id)
- if not is_locked(self, msg.chat.id) and not msg:is_from_admin() then
- api:sendMessage(msg.from.id, text)
+ local text = u:getExtraList(msg.from.chat.id)
+ if not is_locked(self, msg.from.chat.id) and not msg.from:isAdmin() then
+ api:sendMessage(msg.from.user.id, text)
else
msg:send_reply(text)
end
elseif blocks[1] == 'extra del' then
- if not msg:is_from_admin() then return end
+ if not msg.from:isAdmin() then return end
local deleted, not_found, found = {}, {}
- local hash = 'chat:'..msg.chat.id..':extra'
+ local hash = 'chat:'..msg.from.chat.id..':extra'
for extra in blocks[2]:gmatch('(#[%w_]+)') do
found = red:hdel(hash, extra)
if found == 1 then
@@ -120,7 +120,7 @@ function _M:onTextMessage(blocks)
end
msg:send_reply(text, "Markdown")
else
- local chat_id = blocks[1] == 'start' and tonumber(blocks[2]) or msg.chat.id
+ local chat_id = blocks[1] == 'start' and tonumber(blocks[2]) or msg.from.chat.id
local extra = blocks[1] == 'start' and '#'..blocks[3] or blocks[1]
--print(chat_id, extra)
local hash = 'chat:'..chat_id..':extra'
@@ -134,15 +134,15 @@ function _M:onTextMessage(blocks)
local link_preview = text:find('telegra%.ph/') == nil
local _, err
- if msg.chat.id > 0
- or(is_locked(self, msg.chat.id) and not msg:is_from_admin()) then -- send it in private
+ if msg.from.chat.id > 0
+ or(is_locked(self, msg.from.chat.id) and not msg.from:isAdmin()) then -- send it in private
if not file_id then
local reply_markup, clean_text = u:reply_markup_from_text(u:replaceholders(text, msg.reply or msg))
- _, err = api:sendMessage(msg.from.id, clean_text, "Markdown", link_preview, nil, nil, reply_markup)
+ _, err = api:sendMessage(msg.from.user.id, clean_text, "Markdown", link_preview, nil, nil, reply_markup)
elseif special_method then
- _, err = sendMedia(self, msg.from.id, file_id, special_method) -- photo, voices, video need their method to be sent by file_id
+ _, err = sendMedia(self, msg.from.user.id, file_id, special_method) -- photo, voices, video need their method to be sent by file_id
else
- _, err = api:sendDocument(msg.from.id, file_id)
+ _, err = api:sendDocument(msg.from.user.id, file_id)
end
else
local msg_to_reply
@@ -153,19 +153,19 @@ function _M:onTextMessage(blocks)
end
if file_id then
if special_method then
- sendMedia(self, msg.chat.id, file_id, special_method, msg_to_reply) -- photo, voices, video need their method to be sent by file_id
+ sendMedia(self, msg.from.chat.id, file_id, special_method, msg_to_reply) -- photo, voices, video need their method to be sent by file_id
else
- api:sendDocument(msg.chat.id, file_id, nil, nil, msg_to_reply)
+ api:sendDocument(msg.from.chat.id, file_id, nil, nil, msg_to_reply)
end
else
local reply_markup, clean_text = u:reply_markup_from_text(u:replaceholders(text, msg.reply or msg))
- api:sendMessage(msg.chat.id, clean_text, "Markdown", link_preview, nil, msg_to_reply, reply_markup) -- if the admin replies to a user, the bot will reply to the user too
+ api:sendMessage(msg.from.chat.id, clean_text, "Markdown", link_preview, nil, msg_to_reply, reply_markup) -- if the admin replies to a user, the bot will reply to the user too
end
end
- if err and err.error_code == 403 and msg.chat.id < 0 and not u:is_silentmode_on(msg.chat.id) then -- if the user haven't started the bot and silent mode is off
+ if err and err.error_code == 403 and msg.from.chat.id < 0 and not u:is_silentmode_on(msg.from.chat.id) then -- if the user haven't started the bot and silent mode is off
msg:send_reply(i18n("_Please_ [start me](%s) _so I can send you the answer_")
- :format(u:deeplink_constructor(msg.chat.id, extra:sub(2, -1))), "Markdown")
+ :format(u:deeplink_constructor(msg.from.chat.id, extra:sub(2, -1))), "Markdown")
end
end
end
diff --git a/lua/groupbutler/plugins/floodmanager.lua b/lua/groupbutler/plugins/floodmanager.lua
index 839bb2a4f..e6aa94c53 100644
--- a/lua/groupbutler/plugins/floodmanager.lua
+++ b/lua/groupbutler/plugins/floodmanager.lua
@@ -1,5 +1,7 @@
local config = require "groupbutler.config"
local null = require "groupbutler.null"
+local Chat = require("groupbutler.chat")
+local ChatMember = require("groupbutler.chatmember")
local _M = {}
@@ -139,60 +141,66 @@ function _M:onCallbackQuery(blocks)
local u = self.u
local red = self.red
local i18n = self.i18n
- local chat_id = msg.target_id
- if chat_id and not u:is_allowed('config', chat_id, msg.from) then
- api:answerCallbackQuery(msg.cb_id, i18n("You're no longer an admin"))
- else
- local header = i18n([[You can manage the antiflood settings from here.
-It is also possible to choose which type of messages the antiflood will ignore (✅)]])
+ if blocks[1] == "alert" then
+ local text = get_button_description(self, blocks[2])
+ api:answerCallbackQuery(msg.cb_id, text, true, config.bot_settings.cache_time.alert_help)
+ return
+ end
- local text
+ local member = ChatMember:new({
+ chat = Chat:new({id=msg.target_id}, self),
+ user = msg.from.user,
+ }, self)
- if blocks[1] == 'config' then
- text = i18n("Antiflood settings")
- end
+ if not member:can("can_change_info") then
+ api:answerCallbackQuery(msg.cb_id, i18n("Sorry, you don't have permission to change settings"))
+ return
+ end
- if blocks[1] == 'alert' then
- text = get_button_description(self, blocks[2])
- api:answerCallbackQuery(msg.cb_id, text, true, config.bot_settings.cache_time.alert_help)
- return
- end
+ local header = i18n([[You can manage the antiflood settings from here.
- if blocks[1] == 'exc' then
- local media = blocks[2]
- local hash = 'chat:'..chat_id..':floodexceptions'
- local status = red:hget(hash, media)
- if status == 'no' then
- red:hset(hash, media, 'yes')
- text = i18n("❎ [%s] will be ignored by the anti-flood"):format(media)
- else
- red:hset(hash, media, 'no')
- text = i18n("🚫 [%s] won't be ignored by the anti-flood"):format(media)
- end
- end
+It is also possible to choose which type of messages the antiflood will ignore (✅)]])
- local action
- if blocks[1] == 'action' or blocks[1] == 'dim' or blocks[1] == 'raise' then
- if blocks[1] == 'action' then
- action = red:hget('chat:'..chat_id..':flood', 'ActionFlood')
- if action == null then action = config.chat_settings.flood.ActionFlood end
- elseif blocks[1] == 'dim' then
- action = -1
- elseif blocks[1] == 'raise' then
- action = 1
- end
- text = changeFloodSettings(self, chat_id, action)
+ local text
+
+ if blocks[1] == "config" then
+ text = i18n("Antiflood settings")
+ end
+
+ if blocks[1] == "exc" then
+ local media = blocks[2]
+ local hash = "chat:"..member.chat.id..":floodexceptions"
+ local status = red:hget(hash, media)
+ if status == "no" then
+ red:hset(hash, media, "yes")
+ text = i18n("❎ [%s] will be ignored by the anti-flood"):format(media)
+ else
+ red:hset(hash, media, "no")
+ text = i18n("🚫 [%s] won't be ignored by the anti-flood"):format(media)
end
+ end
- if blocks[1] == 'status' then
- text = u:changeSettingStatus(chat_id, 'Flood')
+ local action
+ if blocks[1] == "action" or blocks[1] == "dim" or blocks[1] == "raise" then
+ if blocks[1] == "action" then
+ action = red:hget("chat:"..member.chat.id..":flood", "ActionFlood")
+ if action == null then action = config.chat_settings.flood.ActionFlood end
+ elseif blocks[1] == "dim" then
+ action = -1
+ elseif blocks[1] == "raise" then
+ action = 1
end
+ text = changeFloodSettings(self, member.chat.id, action)
+ end
- local keyboard = do_keyboard_flood(self, chat_id)
- api:editMessageText(msg.chat.id, msg.message_id, nil, header, "Markdown", nil, keyboard)
- api:answerCallbackQuery(msg.cb_id, text)
+ if blocks[1] == "status" then
+ text = u:changeSettingStatus(member.chat.id, "Flood")
end
+
+ local keyboard = do_keyboard_flood(self, member.chat.id)
+ api:editMessageText(msg.from.chat.id, msg.message_id, nil, header, "Markdown", nil, keyboard)
+ api:answerCallbackQuery(msg.cb_id, text)
end
_M.triggers = {
diff --git a/lua/groupbutler/plugins/help.lua b/lua/groupbutler/plugins/help.lua
index 361efa534..e7429d37e 100644
--- a/lua/groupbutler/plugins/help.lua
+++ b/lua/groupbutler/plugins/help.lua
@@ -322,21 +322,23 @@ function _M:onTextMessage(blocks)
local red = self.red
local i18n = self.i18n
if blocks[1] == 'start' then
- if msg.chat.type == 'private' then
- local message = get_helped_string(self, 'start'):format(msg.from.first_name:escape())
+ if msg.from.chat.type == 'private' then
+ local message = get_helped_string(self, 'start'):format(msg.from.user.first_name:escape())
local keyboard = do_keyboard_private(self)
- api:sendMessage(msg.from.id, message, "Markdown", nil, nil, nil, keyboard)
+ api:sendMessage(msg.from.user.id, message, "Markdown", nil, nil, nil, keyboard)
end
end
if blocks[1] == 'help' then
local text = get_helped_string(self, blocks[2] or 'main_menu')
if blocks[2] then
- api:sendMessage(msg.from.id, text, "Markdown")
+ api:sendMessage(msg.from.user.id, text, "Markdown")
else
local keyboard = do_keyboard(self, 'main')
- local res = api:sendMessage(msg.from.id, text, "Markdown", nil, nil, nil, keyboard)
- if not res and msg.chat.type ~= 'private' and red:hget('chat:'..msg.chat.id..':settings', 'Silent') ~= 'on' then
- api:sendMessage(msg.chat.id,
+ local res = api:sendMessage(msg.from.user.id, text, "Markdown", nil, nil, nil, keyboard)
+ if not res
+ and msg.from.chat.type ~= 'private'
+ and red:hget('chat:'..msg.from.chat.id..':settings', 'Silent') ~= 'on' then
+ api:sendMessage(msg.from.chat.id,
i18n('[Start me](%s) _to get the list of commands_'):format(u:deeplink_constructor('', 'help')), "Markdown")
end
end
@@ -380,7 +382,7 @@ function _M:onCallbackQuery(blocks)
query[blocks[1]]()
local keyboard = do_keyboard(self, keyboard_type)
- local ok, err = api:editMessageText(msg.chat.id, msg.message_id, nil, text, "Markdown", nil, keyboard)
+ local ok, err = api:editMessageText(msg.from.chat.id, msg.message_id, nil, text, "Markdown", nil, keyboard)
if not ok and err and err.error_code == 111 then
api:answerCallbackQuery(msg.cb_id, i18n("❗️ Already there"))
end
diff --git a/lua/groupbutler/plugins/links.lua b/lua/groupbutler/plugins/links.lua
index 0e4f8e464..c53c87c1e 100644
--- a/lua/groupbutler/plugins/links.lua
+++ b/lua/groupbutler/plugins/links.lua
@@ -14,26 +14,28 @@ end
function _M:onTextMessage(blocks)
local msg = self.message
- local u = self.u
local red = self.red
local i18n = self.i18n
- if msg.chat.type == 'private' then return end
- if not u:is_allowed('texts', msg.chat.id, msg.from) then return end
- local hash = 'chat:'..msg.chat.id..':links'
+ if msg.from.chat.type == "private"
+ or not msg.from:isAdmin() then
+ return
+ end
+
+ local hash = 'chat:'..msg.from.chat.id..':links'
local text
if blocks[1] == 'link' then
- if msg.chat.username then
- red:sadd('chat:'..msg.chat.id..':whitelist', 'telegram.me/'..msg.chat.username)
- local title = msg.chat.title:escape_hard('link')
- msg:send_reply(string.format('[%s](telegram.me/%s)', title, msg.chat.username), "Markdown")
+ if msg.from.chat.username then
+ red:sadd('chat:'..msg.from.chat.id..':whitelist', 'telegram.me/'..msg.from.chat.username)
+ local title = msg.from.chat.title:escape_hard('link')
+ msg:send_reply(string.format('[%s](telegram.me/%s)', title, msg.from.chat.username), "Markdown")
else
local link = red:hget(hash, 'link')
if link == null then
text = i18n("*No link* for this group. Ask the owner to save it with `/setlink [group link]`")
else
- local title = msg.chat.title:escape_hard('link')
+ local title = msg.from.chat.title:escape_hard('link')
text = string.format('[%s](%s)', title, link)
end
msg:send_reply(text, "Markdown")
@@ -45,9 +47,9 @@ function _M:onTextMessage(blocks)
text = i18n("Link *unset*")
else
local link
- if msg.chat.username then
- link = 'https://telegram.me/'..msg.chat.username
- local substitution = '['..msg.chat.title:escape_hard('link')..']('..link..')'
+ if msg.from.chat.username then
+ link = 'https://telegram.me/'..msg.from.chat.username
+ local substitution = '['..msg.from.chat.title:escape_hard('link')..']('..link..')'
text = i18n("The link has been set.\n*Here's the link*: %s"):format(substitution)
else
if not blocks[2] then
@@ -57,10 +59,10 @@ function _M:onTextMessage(blocks)
text = i18n("This link is *not valid!*")
else
link = 'https://telegram.me/joinchat/'..blocks[2]
- red:sadd('chat:'..msg.chat.id..':whitelist', link:gsub('https://', '')) --save the link in the whitelist
+ red:sadd('chat:'..msg.from.chat.id..':whitelist', link:gsub('https://', '')) --save the link in the whitelist
local succ = red:hset(hash, 'link', link)
- local title = msg.chat.title:escape_hard('link')
+ local title = msg.from.chat.title:escape_hard('link')
local substitution = '['..title..']('..link..')'
if not succ then
text = i18n("The link has been updated.\n*Here's the new link*: %s"):format(substitution)
diff --git a/lua/groupbutler/plugins/logchannel.lua b/lua/groupbutler/plugins/logchannel.lua
index dcf39a767..03d565460 100644
--- a/lua/groupbutler/plugins/logchannel.lua
+++ b/lua/groupbutler/plugins/logchannel.lua
@@ -1,5 +1,7 @@
local config = require "groupbutler.config"
local null = require "groupbutler.null"
+local Chat = require("groupbutler.chat")
+local ChatMember = require("groupbutler.chatmember")
local _M = {}
@@ -101,51 +103,60 @@ end
function _M:onCallbackQuery(blocks)
local api = self.api
local msg = self.message
- local u = self.u
local i18n = self.i18n
+ if blocks[1] == 'alert' then
+ local text = get_alert_text(self, blocks[2])
+ api:answerCallbackQuery(msg.cb_id, text, true, config.bot_settings.cache_time.alert_help)
+ return
+ end
+
+ local chat = Chat:new({id=msg.target_id}, self)
+
if blocks[1] == 'logcb' then
- local chat_id = msg.target_id
- if not msg:is_from_admin() then
+ if not msg.from:isAdmin() then
api:answerCallbackQuery(msg.cb_id, i18n("You are not admin of this group"), true)
- else
- if blocks[2] == 'unban' or blocks[2] == 'untempban' then
- local user_id = blocks[3]
- api:unban_chat_member(chat_id, user_id)
- api:answerCallbackQuery(msg.cb_id, i18n("User unbanned!"), true)
- end
+ return
end
- else
- if blocks[1] == 'alert' then
- local text = get_alert_text(self, blocks[2])
- api:answerCallbackQuery(msg.cb_id, text, true, config.bot_settings.cache_time.alert_help)
- else
- local chat_id = msg.target_id
- if not u:is_allowed('config', chat_id, msg.from) then
- api:answerCallbackQuery(msg.cb_id, i18n("You're no longer an admin"))
- else
- local text
+ if blocks[2] == 'unban' or blocks[2] == 'untempban' then
+ local user_id = blocks[3]
+ api:unbanChatMember(chat.id, user_id)
+ api:answerCallbackQuery(msg.cb_id, i18n("User unbanned!"), true)
+ end
+ return
+ end
- if blocks[1] == 'toggle' then
- toggle_event(self, chat_id, blocks[2])
- text = '👌🏼'
- end
+ local member = ChatMember:new({
+ chat = chat,
+ user = msg.from.user,
+ }, self)
+
+ if not member:can("can_change_info") then
+ api:answerCallbackQuery(msg.cb_id, i18n("Sorry, you don't have permission to change settings"))
+ return
+ end
+
+ local text
- local reply_markup = doKeyboard_logchannel(self, chat_id)
- if blocks[1] == 'config' then
- local logchannel_first = i18n([[*Select the events the will be logged in the channel*
+ if blocks[1] == 'toggle' then
+ toggle_event(self, chat.id, blocks[2])
+ text = '👌🏼'
+ end
+
+ local reply_markup = doKeyboard_logchannel(self, chat.id)
+ if blocks[1] == 'config' then
+ local logchannel_first = i18n([[*Select the events the will be logged in the channel*
✅ = will be logged
☑️ = won't be logged
Tap on an option to get further information]])
- api:edit_message_text(msg.chat.id, msg.message_id, nil, logchannel_first, "Markdown", true, reply_markup)
- else
- api:editMessageReplyMarkup(msg.chat.id, msg.message_id, nil, reply_markup)
- end
+ api:editMessageText(msg.from.chat.id, msg.message_id, nil, logchannel_first, "Markdown", true, reply_markup)
+ else
+ api:editMessageReplyMarkup(msg.from.chat.id, msg.message_id, nil, reply_markup)
+ end
- if text then api:answerCallbackQuery(msg.cb_id, text) end
- end
- end
+ if text then
+ api:answerCallbackQuery(msg.cb_id, text)
end
end
@@ -155,14 +166,14 @@ function _M:onTextMessage(blocks)
local red = self.red
local i18n = self.i18n
- if msg.chat.type ~= 'private' then
- if not msg:is_from_admin() then return end
+ if msg.from.chat.type ~= 'private' then
+ if not msg.from:isAdmin() then return end
if blocks[1] == 'setlog' then
if msg.forward_from_chat then
if msg.forward_from_chat.type == 'channel' then
if not msg.forward_from_chat.username then
- local ok, err = api:getChatMember(msg.forward_from_chat.id, msg.from.id)
+ local ok, err = api:getChatMember(msg.forward_from_chat.id, msg.from.user.id)
if not ok then
if err.error_code == 429 then
msg:send_reply(i18n('_Too many requests. Retry later_'), "Markdown")
@@ -172,18 +183,18 @@ function _M:onTextMessage(blocks)
else
if ok.status == 'creator' then
local text
- local old_log = red:hget('bot:chatlogs', msg.chat.id)
+ local old_log = red:hget('bot:chatlogs', msg.from.chat.id)
if old_log == tostring(msg.forward_from_chat.id) then
text = i18n('_Already using this channel_')
else
- red:hset('bot:chatlogs', msg.chat.id, msg.forward_from_chat.id)
+ red:hset('bot:chatlogs', msg.from.chat.id, msg.forward_from_chat.id)
text = i18n('*Log channel added!*')
if old_log then
api:sendMessage(old_log,
- i18n("%s changed its log channel"):format(msg.chat.title:escape_html()), 'html')
+ i18n("%s changed its log channel"):format(msg.from.chat.title:escape_html()), 'html')
end
api:sendMessage(msg.forward_from_chat.id,
- i18n("Logs of %s will be posted here"):format(msg.chat.title:escape_html()), 'html')
+ i18n("Logs of %s will be posted here"):format(msg.from.chat.title:escape_html()), 'html')
end
msg:send_reply(text, "Markdown")
else
@@ -198,15 +209,15 @@ function _M:onTextMessage(blocks)
msg:send_reply(i18n("You have to *forward* the message from the channel"), "Markdown")
end
elseif blocks[1] == 'unsetlog' then
- local log_channel = red:hget('bot:chatlogs', msg.chat.id)
+ local log_channel = red:hget('bot:chatlogs', msg.from.chat.id)
if log_channel == null then
msg:send_reply(i18n("_This groups is not using a log channel_"), "Markdown")
else
- red:hdel('bot:chatlogs', msg.chat.id)
+ red:hdel('bot:chatlogs', msg.from.chat.id)
msg:send_reply(i18n("*Log channel removed*"), "Markdown")
end
elseif blocks[1] == 'logchannel' then
- local log_channel = red:hget('bot:chatlogs', msg.chat.id)
+ local log_channel = red:hget('bot:chatlogs', msg.from.chat.id)
if log_channel == null then
msg:send_reply(i18n("_This groups is not using a log channel_"), "Markdown")
else
@@ -226,7 +237,7 @@ function _M:onTextMessage(blocks)
end
end
elseif blocks[1] == 'photo' then
- api:sendPhoto(msg.chat.id, blocks[2])
+ api:sendPhoto(msg.from.chat.id, blocks[2])
end
end
@@ -245,7 +256,7 @@ _M.triggers = {
--callbacks from the configuration keyboard
'^###cb:logchannel:(toggle):([%w_]+):(-?%d+)$',
- '^###cb:logchannel:(alert):([%w_]+):([%w_]+)$',
+ '^###cb:logchannel:(alert):([%w_]+)$',
'^###cb:(config):logchannel:(-?%d+)$'
}
}
diff --git a/lua/groupbutler/plugins/mediasettings.lua b/lua/groupbutler/plugins/mediasettings.lua
index db0f5041c..127eeecab 100644
--- a/lua/groupbutler/plugins/mediasettings.lua
+++ b/lua/groupbutler/plugins/mediasettings.lua
@@ -1,5 +1,7 @@
local config = require "groupbutler.config"
local null = require "groupbutler.null"
+local Chat = require("groupbutler.chat")
+local ChatMember = require("groupbutler.chatmember")
local _M = {}
@@ -109,73 +111,81 @@ function _M:onCallbackQuery(blocks)
local msg = self.message
local red = self.red
local i18n = self.i18n
- local u = self.u
- local chat_id = msg.target_id
- if chat_id and not u:is_allowed('config', chat_id, msg.from) then
- api:answerCallbackQuery(msg.cb_id, i18n("You're no longer an admin"))
- else
- local media_first = i18n([[
-Tap on an option on the right to *change the setting*
+
+ if blocks[1] == "mediallert" then
+ api:answerCallbackQuery(msg.cb_id, i18n("⚠️ Tap on the right column"), false,
+ config.bot_settings.cache_time.alert_help)
+ return
+ end
+
+ local member = ChatMember:new({
+ chat = Chat:new({id=msg.target_id}, self),
+ user = msg.from.user,
+ }, self)
+
+ if not member:can("can_change_info") then
+ api:answerCallbackQuery(msg.cb_id, i18n("Sorry, you don't have permission to change settings"))
+ return
+ end
+
+ local media_first = i18n([[Tap on an option on the right to *change the setting*
You can use the last lines to change how many warnings the bot should give before kicking/banning/muting someone.
The number is not related the the normal `/warn` command.
Possible statuses: ✅ allowed, ❌ warning, 🗑 delete.
-When a media is set to delete, the bot will give a warning *only* when this is the users last warning
-]])
-
- if blocks[1] == 'config' then
- local keyboard = doKeyboard_media(self, chat_id)
- api:editMessageText(msg.chat.id, msg.message_id, nil, media_first, "Markdown", nil, keyboard)
- else
- if blocks[1] == 'mediallert' then
- api:answerCallbackQuery(msg.cb_id, i18n("⚠️ Tap on the right column"), false,
- config.bot_settings.cache_time.alert_help)
- return
- end
- local cb_text
- if blocks[1] == 'mediawarn' then
- local current = tonumber(red:hget('chat:'..chat_id..':warnsettings', 'mediamax')) or 2
- if blocks[2] == 'dim' then
- if current < 2 then
- cb_text = i18n("⚙ The new value is too low ( < 1)")
- else
- local new = red:hincrby('chat:'..chat_id..':warnsettings', 'mediamax', -1)
- cb_text = string.format('⚙ %d → %d', current, new)
- end
- elseif blocks[2] == 'raise' then
- if current > 11 then
- cb_text = i18n("⚙ The new value is too high ( > 12)")
- else
- local new = red:hincrby('chat:'..chat_id..':warnsettings', 'mediamax', 1)
- cb_text = string.format('⚙ %d → %d', current, new)
- end
- end
- end
- if blocks[1] == 'mediatype' then
- local hash = 'chat:'..chat_id..':warnsettings'
- local current = red:hget(hash, 'mediatype')
- if current == null then current = config.chat_settings['warnsettings']['mediatype'] end
-
- if current == 'ban' then
- red:hset(hash, 'mediatype', 'kick')
- cb_text = i18n("👞 New status is kick")
- elseif current == 'kick' then
- red:hset(hash, 'mediatype', 'mute')
- cb_text = i18n("👁 New status is mute")
- elseif current == 'mute' then
- red:hset(hash, 'mediatype', 'ban')
- cb_text = i18n("🔨 New status is ban")
- end
+When a media is set to delete, the bot will give a warning *only* when this is the users last warning]])
+
+ if blocks[1] == "config" then
+ local keyboard = doKeyboard_media(self, member.chat.id)
+ api:editMessageText(msg.from.chat.id, msg.message_id, nil, media_first, "Markdown", nil, keyboard)
+ return
+ end
+
+ local cb_text
+ if blocks[1] == "mediawarn" then
+ local current = tonumber(red:hget("chat:"..member.chat.id..":warnsettings", "mediamax")) or 2
+ if blocks[2] == "dim" then
+ if current < 2 then
+ cb_text = i18n("⚙ The new value is too low ( < 1)")
+ else
+ local new = red:hincrby("chat:"..member.chat.id..":warnsettings", "mediamax", -1)
+ cb_text = string.format("⚙ %d → %d", current, new)
end
- if blocks[1] == 'media' then
- local media = blocks[2]
- cb_text = change_media_status(self, chat_id, media)
+ elseif blocks[2] == "raise" then
+ if current > 11 then
+ cb_text = i18n("⚙ The new value is too high ( > 12)")
+ else
+ local new = red:hincrby("chat:"..member.chat.id..":warnsettings", "mediamax", 1)
+ cb_text = string.format("⚙ %d → %d", current, new)
end
- local keyboard = doKeyboard_media(self, chat_id)
- api:editMessageText(msg.chat.id, msg.message_id, nil, media_first, "Markdown", nil, keyboard)
- api:answerCallbackQuery(msg.cb_id, cb_text)
end
end
+
+ if blocks[1] == "mediatype" then
+ local hash = "chat:"..member.chat.id..":warnsettings"
+ local current = red:hget(hash, "mediatype")
+ if current == null then current = config.chat_settings["warnsettings"]["mediatype"] end
+
+ if current == "ban" then
+ red:hset(hash, "mediatype", "kick")
+ cb_text = i18n("👞 New status is kick")
+ elseif current == "kick" then
+ red:hset(hash, "mediatype", "mute")
+ cb_text = i18n("👁 New status is mute")
+ elseif current == "mute" then
+ red:hset(hash, "mediatype", "ban")
+ cb_text = i18n("🔨 New status is ban")
+ end
+ end
+
+ if blocks[1] == "media" then
+ local media = blocks[2]
+ cb_text = change_media_status(self, member.chat.id, media)
+ end
+
+ local keyboard = doKeyboard_media(self, member.chat.id)
+ api:editMessageText(msg.from.chat.id, msg.message_id, nil, media_first, "Markdown", nil, keyboard)
+ api:answerCallbackQuery(msg.cb_id, cb_text)
end
_M.triggers = {
@@ -183,7 +193,7 @@ _M.triggers = {
'^###cb:(media):([%a_]+):(-?%d+)',
'^###cb:(mediatype):(-?%d+)',
'^###cb:(mediawarn):(%a+):(-?%d+)',
- '^###cb:(mediallert):([%w_]+)$',
+ '^###cb:(mediallert)$',
'^###cb:(config):media:(-?%d+)$'
}
diff --git a/lua/groupbutler/plugins/menu.lua b/lua/groupbutler/plugins/menu.lua
index ff8d1f192..78f5870e7 100644
--- a/lua/groupbutler/plugins/menu.lua
+++ b/lua/groupbutler/plugins/menu.lua
@@ -1,5 +1,7 @@
local config = require "groupbutler.config"
local null = require "groupbutler.null"
+local Chat = require("groupbutler.chat")
+local ChatMember = require("groupbutler.chatmember")
local _M = {}
@@ -253,49 +255,55 @@ function _M:onCallbackQuery(blocks)
local msg = self.message
local u = self.u
local i18n = self.i18n
- local chat_id = msg.target_id
- if chat_id and not u:is_allowed('config', chat_id, msg.from) then
- api:answerCallbackQuery(msg.cb_id, i18n("You're no longer an admin"))
- else
- local menu_first = i18n("Manage the settings of the group. Click on the left column to get a small hint")
- local keyboard, text, show_alert
+ if blocks[2] == "alert" then
+ local text = get_button_description(self, blocks[3])
+ api:answerCallbackQuery(msg.cb_id, text, true, config.bot_settings.cache_time.alert_help)
+ return
+ end
- if blocks[1] == 'config' then
- keyboard = doKeyboard_menu(self, chat_id)
- api:editMessageText(msg.chat.id, msg.message_id, nil, menu_first, "Markdown", nil, keyboard)
- else
- if blocks[2] == 'alert' then
- text = get_button_description(self, blocks[3])
- api:answerCallbackQuery(msg.cb_id, text, true, config.bot_settings.cache_time.alert_help)
- return
- end
- if blocks[2] == 'DimWarn' or blocks[2] == 'RaiseWarn' or blocks[2] == 'ActionWarn' then
- if blocks[2] == 'DimWarn' then
- text = changeWarnSettings(self, chat_id, -1)
- elseif blocks[2] == 'RaiseWarn' then
- text = changeWarnSettings(self, chat_id, 1)
- elseif blocks[2] == 'ActionWarn' then
- text = changeWarnSettings(self, chat_id, 'status')
- end
- elseif blocks[2] == 'Rtl' or blocks[2] == 'Arab' then
- text = changeCharSettings(self, chat_id, blocks[2])
- else
- text, show_alert = u:changeSettingStatus(chat_id, blocks[2])
- end
- keyboard = doKeyboard_menu(self, chat_id)
- api:editMessageText(msg.chat.id, msg.message_id, nil, menu_first, "Markdown", nil, keyboard)
- if text then
- --workaround to avoid to send an error to users who are using an old inline keyboard
- api:answerCallbackQuery(msg.cb_id, '⚙ '..text, show_alert)
- end
- end
+ local member = ChatMember:new({
+ chat = Chat:new({id=msg.target_id}, self),
+ user = msg.from.user,
+ }, self)
+
+ if not member:can("can_change_info") then
+ api:answerCallbackQuery(msg.cb_id, i18n("Sorry, you don't have permission to change settings"))
+ return
+ end
+
+ local menu_first = i18n("Manage the settings of the group. Click on the left column to get a small hint")
+
+ local keyboard, text, show_alert
+
+ if blocks[1] == "config" then
+ keyboard = doKeyboard_menu(self, member.chat.id)
+ api:editMessageText(msg.from.chat.id, msg.message_id, nil, menu_first, "Markdown", nil, keyboard)
+ return
+ end
+
+ if blocks[2] == "DimWarn" then
+ text = changeWarnSettings(self, member.chat.id, -1)
+ elseif blocks[2] == "RaiseWarn" then
+ text = changeWarnSettings(self, member.chat.id, 1)
+ elseif blocks[2] == "ActionWarn" then
+ text = changeWarnSettings(self, member.chat.id, "status")
+ elseif blocks[2] == "Rtl" or blocks[2] == "Arab" then
+ text = changeCharSettings(self, member.chat.id, blocks[2])
+ else
+ text, show_alert = u:changeSettingStatus(member.chat.id, blocks[2])
+ end
+ keyboard = doKeyboard_menu(self, member.chat.id)
+ api:editMessageText(msg.from.chat.id, msg.message_id, nil, menu_first, "Markdown", nil, keyboard)
+ if text then
+ --workaround to avoid to send an error to users who are using an old inline keyboard
+ api:answerCallbackQuery(msg.cb_id, "⚙ "..text, show_alert)
end
end
_M.triggers = {
onCallbackQuery = {
- '^###cb:(menu):(alert):settings:([%w_]+):([%w_]+)$',
+ '^###cb:(menu):(alert):settings:([%w_]+)$',
'^###cb:(menu):(.*):',
'^###cb:(config):menu:(-?%d+)$'
}
diff --git a/lua/groupbutler/plugins/onmessage.lua b/lua/groupbutler/plugins/onmessage.lua
index 5cbc7b089..b1819dc7e 100644
--- a/lua/groupbutler/plugins/onmessage.lua
+++ b/lua/groupbutler/plugins/onmessage.lua
@@ -38,7 +38,7 @@ local function is_flooding_funct(self, msg)
if msg.media_group_id then
-- albums should count as one message
- local media_group_id_key = 'mediagroupidkey:'..msg.chat.id
+ local media_group_id_key = 'mediagroupidkey:'..msg.from.chat.id
if msg.media_group_id == red:get(media_group_id_key) then -- msg.media_group_id is a str
-- photo/video is from an already processed sent album
return false
@@ -49,11 +49,11 @@ local function is_flooding_funct(self, msg)
end
end
- local spamkey = 'spam:'..msg.chat.id..':'..msg.from.id
+ local spamkey = 'spam:'..msg.from.chat.id..':'..msg.from.user.id
local msgs = tonumber(red:get(spamkey)) or 1
- local max_msgs = tonumber(red:hget('chat:'..msg.chat.id..':flood', 'MaxFlood')) or 5
+ local max_msgs = tonumber(red:hget('chat:'..msg.from.chat.id..':flood', 'MaxFlood')) or 5
if msg.cb then max_msgs = 15 end
local max_time = 5
@@ -91,23 +91,23 @@ function _M:on_message()
local msg_type = msg:type()
if msg.forward_from or msg.forward_from_chat then msg_type = 'forward' end
- if not is_ignored(self, msg.chat.id, msg_type) and not msg.edited then
+ if not is_ignored(self, msg.from.chat.id, msg_type) and not msg.edited then
local is_flooding, msgs_sent, msgs_max = is_flooding_funct(self, msg)
if is_flooding then
- local status = red:hget('chat:'..msg.chat.id..':settings', 'Flood')
+ local status = red:hget('chat:'..msg.from.chat.id..':settings', 'Flood')
if status == null then status = config.chat_settings['settings']['Flood'] end
- if status == 'on' and not msg.cb and not msg:is_from_admin() then --if the status is on, and the user is not an admin, and the message is not a callback, then:
- local action = red:hget('chat:'..msg.chat.id..':flood', 'ActionFlood')
- local name = u:getname_final(msg.from)
+ if status == 'on' and not msg.cb and not msg.from:isAdmin() then --if the status is on, and the user is not an admin, and the message is not a callback, then:
+ local action = red:hget('chat:'..msg.from.chat.id..':flood', 'ActionFlood')
+ local name = msg.from.user:getLink()
local ok, message
--try to kick or ban
if action == 'ban' then
- ok = u:banUser(msg.chat.id, msg.from.id)
+ ok = msg.from:ban()
elseif action == 'kick' then
- ok = u:kickUser(msg.chat.id, msg.from.id)
+ ok = msg.from:kick()
elseif action == 'mute' then
- ok = u:muteUser(msg.chat.id, msg.from.id)
+ ok = msg.from:mute()
end
--if kicked/banned, send a message
if ok then
@@ -120,7 +120,7 @@ function _M:on_message()
elseif action == 'mute' then
message = i18n("%s muted for flood!"):format(name)
end
- api:sendMessage(msg.chat.id, message, 'html')
+ api:sendMessage(msg.from.chat.id, message, 'html')
u:logEvent('flood', msg, {hammered = log_hammered})
end
end
@@ -134,45 +134,45 @@ function _M:on_message()
end
if msg_type ~= "text" and not msg.cb and not msg.edited then
- local hash = 'chat:'..msg.chat.id..':media'
+ local hash = 'chat:'..msg.from.chat.id..':media'
local media_status = (red:hget(hash, msg_type))
if media_status == null then media_status = config.chat_settings.media[msg_type] end
if media_status == 'notok' then
local whitelisted
if msg_type == 'link' then
- whitelisted = is_whitelisted(self, msg.chat.id, msg.text)
+ whitelisted = is_whitelisted(self, msg.from.chat.id, msg.text)
end
- if not whitelisted and not msg:is_from_admin() then -- Postpone admin check to avoid hitting API limits
+ if not whitelisted and not msg.from:isAdmin() then -- Postpone admin check to avoid hitting API limits
local status
- local name = u:getname_final(msg.from)
- local max_reached_var, n, max = max_reached(self, msg.chat.id, msg.from.id)
+ local name = msg.from.user:getLink()
+ local max_reached_var, n, max = max_reached(self, msg.from.chat.id, msg.from.user.id)
if max_reached_var then --max num reached. Kick/ban the user
- status = red:hget('chat:'..msg.chat.id..':warnsettings', 'mediatype')
+ status = red:hget('chat:'..msg.from.chat.id..':warnsettings', 'mediatype')
if status == null then status = config.chat_settings['warnsettings']['mediatype'] end
--try to kick/ban
local ok, punishment
if status == 'kick' then
- ok = u:kickUser(msg.chat.id, msg.from.id)
+ ok = msg.from:kick()
punishment = i18n('kicked')
elseif status == 'ban' then
- ok = u:banUser(msg.chat.id, msg.from.id)
+ ok = msg.from:ban()
punishment = i18n('banned')
elseif status == 'mute' then
- ok = u:muteUser(msg.chat.id, msg.from.id)
+ ok = msg.from:mute()
punishment = i18n('muted')
end
if ok then --kick worked
- red:hdel('chat:'..msg.chat.id..':mediawarn', msg.from.id) --remove media warns
+ red:hdel('chat:'..msg.from.chat.id..':mediawarn', msg.from.user.id) --remove media warns
local message =
i18n('%s %s: media sent not allowed!\n❗️ %d/%d
'):format(name, punishment, n, max)
- api:sendMessage(msg.chat.id, message, 'html')
+ api:sendMessage(msg.from.chat.id, message, 'html')
end
if media_status == 'del' then --do not forget to delete the message
- api:deleteMessage(msg.chat.id, msg.message_id)
+ api:deleteMessage(msg.from.chat.id, msg.message_id)
end
else --max num not reached -> warn or delete
if media_status ~= 'del' then
@@ -180,14 +180,14 @@ function _M:on_message()
i18n('%s, this type of media is not allowed in this chat.\n(%d/%d
)'):format(name, n, max)
msg:send_reply(message, 'html')
elseif media_status == 'del' and n + 1 >= max then
- api:deleteMessage(msg.chat.id, msg.message_id)
+ api:deleteMessage(msg.from.chat.id, msg.message_id)
local message =
i18n([[%s, this type of media is not allowed in this chat.
The next time you will be banned/kicked/muted
]]):format(name)
- api:sendMessage(msg.chat.id, message, 'html')
+ api:sendMessage(msg.from.chat.id, message, 'html')
elseif media_status == 'del' then
- api:deleteMessage(msg.chat.id, msg.message_id)
+ api:deleteMessage(msg.from.chat.id, msg.message_id)
end
end
u:logEvent('mediawarn', msg, {warns = n, warnmax = max, media = msg_type, hammered = status})
@@ -195,60 +195,60 @@ function _M:on_message()
end
end
- local rtl_status = red:hget('chat:'..msg.chat.id..':char', 'Rtl')
+ local rtl_status = red:hget('chat:'..msg.from.chat.id..':char', 'Rtl')
if rtl_status == null then rtl_status = config.chat_settings.char.Rtl end
if rtl_status ~= 'allowed' then
local rtl = ''
local last_name = 'x'
- if msg.from.last_name then last_name = msg.from.last_name end
- local check = msg.text:find(rtl..'+') or msg.from.first_name:find(rtl..'+') or last_name:find(rtl..'+')
+ if msg.from.user.last_name then last_name = msg.from.user.last_name end
+ local check = msg.text:find(rtl..'+') or msg.from.user.first_name:find(rtl..'+') or last_name:find(rtl..'+')
if check ~= nil then
local ok, message
if rtl_status == 'kick' then
- ok = u:kickUser(msg.chat.id, msg.from.id)
+ ok = msg.from:kick()
message = i18n("%s kicked: RTL character in names/messages are not allowed!")
elseif rtl_status == 'ban' then
- ok = u:banUser(msg.chat.id, msg.from.id)
+ ok = msg.from:ban()
message = i18n("%s banned: RTL character in names/messages are not allowed!")
elseif rtl_status == 'mute' then
- ok = u:muteUser(msg.chat.id, msg.from.id)
+ ok = msg.from:mute()
message = i18n("%s muted: RTL character in names/messages are not allowed!")
end
if ok then
- local name = u:getname_final(msg.from)
- api:sendMessage(msg.chat.id, message:format(name), 'html')
+ local name = msg.from.user:getLink()
+ api:sendMessage(msg.from.chat.id, message:format(name), 'html')
return false
end
end
end
if msg.text and msg.text:find('([\216-\219][\128-\191])') then
- local arab_status = red:hget('chat:'..msg.chat.id..':char', 'Arab')
+ local arab_status = red:hget('chat:'..msg.from.chat.id..':char', 'Arab')
if arab_status == null then arab_status = config.chat_settings.char.Arab end
if arab_status ~= 'allowed' then
local ok, message
if arab_status == 'kick' then
- ok = u:kickUser(msg.chat.id, msg.from.id)
+ ok = msg.from:kick()
message = i18n("%s kicked: arab/persian message detected!")
elseif arab_status == 'ban' then
- ok = u:banUser(msg.chat.id, msg.from.id)
+ ok = msg.from:ban()
message = i18n("%s banned: arab/persian message detected!")
elseif arab_status == 'mute' then
- ok = u:muteUser(msg.chat.id, msg.from.id)
+ ok = msg.from:mute()
message = i18n("%s muted: arab/persian message detected!")
end
if ok then
- local name = u:getname_final(msg.from)
- api:sendMessage(msg.chat.id, message:format(name), 'html')
+ local name = msg.from.user:getLink()
+ api:sendMessage(msg.from.chat.id, message:format(name), 'html')
return false
end
end
end
end
- if u:is_blocked_global(msg.from.id) then --ignore blocked users
+ if u:is_blocked_global(msg.from.user.id) then --ignore blocked users
return false -- if a user is blocked, don't go through plugins
end
diff --git a/lua/groupbutler/plugins/pin.lua b/lua/groupbutler/plugins/pin.lua
index 8f6160064..b72d6e1e6 100644
--- a/lua/groupbutler/plugins/pin.lua
+++ b/lua/groupbutler/plugins/pin.lua
@@ -25,7 +25,7 @@ local function new_pin(self, msg, pin_text)
local api_err = self.api_err
local reply_markup, text = get_reply_markup(self, msg, pin_text)
local ok, err = self.api:send_message{
- chat_id = msg.chat.id,
+ chat_id = msg.from.chat.id,
text = text,
parse_mode = "Markdown",
disable_web_page_preview = true,
@@ -37,20 +37,20 @@ local function new_pin(self, msg, pin_text)
return
end
- pin_message(self, msg.chat.id, ok.message_id)
+ pin_message(self, msg.from.chat.id, ok.message_id)
return
end
local function edit_pin(self, msg, pin_text)
local api_err = self.api_err
- local pin_id = self.red:get("chat:"..msg.chat.id..":pin")
+ local pin_id = self.red:get("chat:"..msg.from.chat.id..":pin")
if pin_id == null then
new_pin(self, msg, pin_text)
return
end
local reply_markup, text = get_reply_markup(self, msg, pin_text)
local ok, err = self.api:edit_message_text{
- chat_id = msg.chat.id,
+ chat_id = msg.from.chat.id,
message_id = pin_id,
text = text,
parse_mode = "Markdown",
@@ -65,26 +65,26 @@ local function edit_pin(self, msg, pin_text)
msg:send_reply(api_err:trans(err), "Markdown")
return
end
- pin_message(self, msg.chat.id, ok.message_id)
+ pin_message(self, msg.from.chat.id, ok.message_id)
return
end
local function last_pin(self, msg)
local i18n = self.i18n
- local pin_id = self.red:get("chat:"..msg.chat.id..":pin")
+ local pin_id = self.red:get("chat:"..msg.from.chat.id..":pin")
if pin_id == null then
msg:send_reply(i18n("I couldn't find any message generated by /pin
."), "html")
return
end
local ok, err = self.api:send_message{
- chat_id = msg.chat.id,
+ chat_id = msg.from.chat.id,
text = i18n("Last message generated by /pin
^"),
parse_mode = "html",
reply_to_message_id = pin_id,
}
if not ok and err.description:lower():match("reply message not found") then
msg:send_reply(i18n("The old message generated with /pin
does not exist anymore."), "html")
- self.red:del("chat:"..msg.chat.id..":pin")
+ self.red:del("chat:"..msg.from.chat.id..":pin")
return
end
end
@@ -92,8 +92,8 @@ end
function _M:onTextMessage(blocks)
local msg = self.message
- if msg.chat.type == "private"
- or not msg:is_from_admin() then
+ if msg.from.chat.type == "private"
+ or not msg.from:isAdmin() then
return
end
diff --git a/lua/groupbutler/plugins/private.lua b/lua/groupbutler/plugins/private.lua
index e77a60146..8d527383e 100644
--- a/lua/groupbutler/plugins/private.lua
+++ b/lua/groupbutler/plugins/private.lua
@@ -55,24 +55,24 @@ function _M:onTextMessage(blocks)
local i18n = self.i18n
local api_err = self.api_err
- if msg.chat.type ~= 'private' then return end
+ if msg.from.chat.type ~= 'private' then return end
if blocks[1] == 'ping' then
- api:sendMessage(msg.from.id, i18n("Pong!"), "Markdown")
+ api:sendMessage(msg.from.user.id, i18n("Pong!"), "Markdown")
end
if blocks[1] == 'echo' then
- local ok, err = api:sendMessage(msg.chat.id, blocks[2], "Markdown")
+ local ok, err = api:sendMessage(msg.from.chat.id, blocks[2], "Markdown")
if not ok then
- api:sendMessage(msg.chat.id, api_err:trans(err), "Markdown")
+ api:sendMessage(msg.from.chat.id, api_err:trans(err), "Markdown")
end
end
if blocks[1] == 'about' then
local keyboard = do_keyboard_credits(self)
- api:sendMessage(msg.chat.id, strings(self).about, "Markdown", true, nil, nil, keyboard)
+ api:sendMessage(msg.from.chat.id, strings(self).about, "Markdown", true, nil, nil, keyboard)
end
if blocks[1] == 'group' then
if config.help_group and config.help_group ~= '' then
- api:sendMessage(msg.chat.id,
+ api:sendMessage(msg.from.chat.id,
i18n('You can find the list of our support groups in [this channel](%s)'):format(config.help_group), "Markdown")
end
end
@@ -85,12 +85,12 @@ function _M:onCallbackQuery(blocks)
if blocks[1] == 'about' then
local keyboard = do_keyboard_credits(self)
- api:editMessageText(msg.chat.id, msg.message_id, nil, strings(self).about, "Markdown", true, keyboard)
+ api:editMessageText(msg.from.chat.id, msg.message_id, nil, strings(self).about, "Markdown", true, keyboard)
end
if blocks[1] == 'group' then
if config.help_group and config.help_group ~= '' then
local markup = {inline_keyboard={{{text = i18n('🔙 back'), callback_data = 'fromhelp:about'}}}}
- api:editMessageText(msg.chat.id, msg.message_id, nil,
+ api:editMessageText(msg.from.chat.id, msg.message_id, nil,
i18n("You can find the list of our support groups in [this channel](%s)"):format(config.help_group),
"Markdown", nil, markup)
end
diff --git a/lua/groupbutler/plugins/private_settings.lua b/lua/groupbutler/plugins/private_settings.lua
index 7d509595b..50b5f3fd2 100644
--- a/lua/groupbutler/plugins/private_settings.lua
+++ b/lua/groupbutler/plugins/private_settings.lua
@@ -54,10 +54,10 @@ function _M:onTextMessage()
local api = self.api
local msg = self.message
local i18n = self.i18n
- if msg.chat.type == 'private' then
- local reply_markup = doKeyboard_privsett(self, msg.from.id)
+ if msg.from.chat.type == 'private' then
+ local reply_markup = doKeyboard_privsett(self, msg.from.user.id)
api:send_message{
- chat_id = msg.from.id,
+ chat_id = msg.from.user.id,
text = i18n("Change your private settings"),
reply_markup = reply_markup
}
@@ -72,10 +72,10 @@ function _M:onCallbackQuery(blocks)
api:answerCallbackQuery(msg.cb_id, get_button_description(self, blocks[2]), true)
return
end
- self.db:toggle_user_setting(msg.from.id, blocks[2])
- local reply_markup = doKeyboard_privsett(self, msg.from.id)
+ self.db:toggle_user_setting(msg.from.user.id, blocks[2])
+ local reply_markup = doKeyboard_privsett(self, msg.from.user.id)
api:edit_message_reply_markup{
- chat_id = msg.from.id,
+ chat_id = msg.from.user.id,
message_id = msg.message_id,
reply_markup = reply_markup
}
diff --git a/lua/groupbutler/plugins/report.lua b/lua/groupbutler/plugins/report.lua
index b21a57f34..cfeced62e 100644
--- a/lua/groupbutler/plugins/report.lua
+++ b/lua/groupbutler/plugins/report.lua
@@ -1,5 +1,6 @@
local config = require "groupbutler.config"
local null = require "groupbutler.null"
+local api_u = require("telegram-bot-api.utilities")
local _M = {}
@@ -22,26 +23,26 @@ end
local function report(self, msg, description)
local api = self.api
local red = self.red
+ local db = self.db
local i18n = self.i18n
- local u = self.u
local text = i18n(
- '• Message reported by: %s (%d
)'):format(u:getname_final(msg.from), msg.from.id)
- local chat_link = red:hget('chat:'..msg.chat.id..':links', 'link')
+ '• Message reported by: %s (%d
)'):format(msg.from.user:getLink(), msg.from.user.id)
+ local chat_link = red:hget('chat:'..msg.from.chat.id..':links', 'link')
if msg.reply.forward_from or msg.reply.forward_from_chat or msg.reply.sticker then
text = text..i18n(
'\n• Reported message sent by: %s (%d
)'
- ):format(u:getname_final(msg.reply.from), msg.reply.from.id)
+ ):format(msg.reply.from.user:getLink(), msg.reply.from.user.id)
end
if chat_link == null then
- text = text..i18n('\n• Group: %s'):format(msg.chat.title:escape_html())
+ text = text..i18n('\n• Group: %s'):format(msg.from.chat.title:escape_html())
else
- text = text..i18n('\n• Group: %s'):format(chat_link, msg.chat.title:escape_html())
+ text = text..i18n('\n• Group: %s'):format(chat_link, msg.from.chat.title:escape_html())
end
- if msg.chat.username then
+ if msg.from.chat.username then
text = text..i18n(
'\n• Go to the message'
- ):format('telegram.me/'..msg.chat.username..'/'..msg.message_id)
+ ):format('telegram.me/'..msg.from.chat.username..'/'..msg.message_id)
end
if description then
text = text..i18n('\n• Description: %s'):format(description:escape_html())
@@ -49,20 +50,18 @@ local function report(self, msg, description)
local n = 0
- local admins_list = u:get_cached_admins_list(msg.chat.id)
+ local admins_list = db:getChatAdministratorsList(msg.from.chat)
if not admins_list then return false end
- local desc_msg
- local markup = {inline_keyboard={{{text = i18n("Address this report")}}}}
- local callback_data = ("report:%d:"):format(msg.chat.id)
- local hash = 'chat:'..msg.chat.id..':report:'..msg.message_id --stores the user_id and the msg_id of the report messages sent to the admins
+ local callback_data = ("report:%d:"):format(msg.from.chat.id)
+ local hash = 'chat:'..msg.from.chat.id..':report:'..msg.message_id --stores the user_id and the msg_id of the report messages sent to the admins
for i=1, #admins_list do
- local receive_reports = red:hget('user:'..admins_list[i]..':settings', 'reports')
- if receive_reports ~= null and receive_reports == 'on' then
- local res_fwd = api:forwardMessage(admins_list[i], msg.chat.id, msg.reply.message_id)
+ if db:get_user_setting(admins_list[i], "reports") then
+ local res_fwd = api:forwardMessage(admins_list[i], msg.from.chat.id, msg.reply.message_id)
if res_fwd then
- markup.inline_keyboard[1][1].callback_data = callback_data..(msg.message_id)
- desc_msg = api:sendMessage(admins_list[i], text, 'html', true, nil, res_fwd.message_id, markup)
+ local reply_markup = api_u.InlineKeyboardMarkup:new()
+ :row({text = i18n("Address this report"), callback_data = callback_data..msg.message_id})
+ local desc_msg = api:sendMessage(admins_list[i], text, 'html', true, nil, res_fwd.message_id, reply_markup)
if desc_msg then
red:hset(hash, admins_list[i], desc_msg.message_id) --save the msg_id of the msg sent to the admin
n = n + 1
@@ -100,8 +99,9 @@ function _M:onTextMessage(blocks)
local i18n = self.i18n
local u = self.u
- if msg.chat.id < 0 then
- if #blocks > 1 and u:is_allowed('config', msg.chat.id, msg.from) then
+ if msg.from.chat.id < 0 then
+ if #blocks > 1
+ and msg.from:isAdmin() then
local times_allowed, duration = tonumber(blocks[2]), tonumber(blocks[3])
local text
if times_allowed < 1 or times_allowed > 1000 then
@@ -109,7 +109,7 @@ function _M:onTextMessage(blocks)
elseif duration < 1 or duration > 10080 then
text = i18n("_Invalid value:_ time (`input: %d`)"):format(duration)
else
- local hash = 'chat:'..msg.chat.id..':report'
+ local hash = 'chat:'..msg.from.chat.id..':report'
red:hset(hash, 'times_allowed', times_allowed)
red:hset(hash, 'duration', (duration * 60))
text = i18n(
@@ -118,21 +118,21 @@ function _M:onTextMessage(blocks)
end
msg:send_reply(text, "Markdown")
else
- if not msg.reply or msg:is_from_admin() then
+ if not msg.reply or msg.from:isAdmin() then
return
end
- local status = red:hget('chat:'..msg.chat.id..':settings', 'Reports')
+ local status = red:hget('chat:'..msg.from.chat.id..':settings', 'Reports')
if status == null then status = config.chat_settings['settings']['Reports'] end
if status == 'off' then return end
local text
- if user_is_abusing(self, msg.chat.id, msg.from.id) then
- local hash = 'chat:'..msg.chat.id..':report'
+ if user_is_abusing(self, msg.from.chat.id, msg.from.user.id) then
+ local hash = 'chat:'..msg.from.chat.id..':report'
local duration = tonumber(red:hget(hash, 'duration')) or config.bot_settings.report.duration
local times_allowed = tonumber(red:hget(hash, 'times_allowed')) or config.bot_settings.report.times_allowed
- local ttl = red:ttl(hash..':'..msg.from.id)
+ local ttl = red:ttl(hash..':'..msg.from.user.id)
local minutes, seconds = seconds2minutes(ttl)
text = i18n([[_Please, do not abuse this command. It can be used %d times every %d minutes_.
Wait other %d minutes, %d seconds.]]):format(times_allowed, (duration / 60), minutes, seconds)
@@ -177,7 +177,7 @@ function _M:onCallbackQuery(blocks)
if addressed_by == null then
--no one addressed the issue yet
- local name = msg.from.first_name:sub(1, 120)
+ local name = msg.from.user.first_name:sub(1, 120)
local chats_reached = red:hgetall(hash)
if next(chats_reached) then
local markup = {inline_keyboard={
@@ -188,7 +188,7 @@ function _M:onCallbackQuery(blocks)
api:editMessageReplyMarkup(user_id, message_id, markup)
end
table.insert(markup.inline_keyboard, close_issue_line)
- api:editMessageReplyMarkup(msg.from.id, msg.message_id, markup)
+ api:editMessageReplyMarkup(msg.from.user.id, msg.message_id, markup)
end
red:setex(hash..':addressed', 3600*24*2, name)
api:answerCallbackQuery(msg.cb_id, "✅")
@@ -196,7 +196,7 @@ function _M:onCallbackQuery(blocks)
api:answerCallbackQuery(msg.cb_id, i18n("%s has/will address this report"):format(addressed_by), true, 48 * 3600)
end
elseif blocks[1] == 'close' then
- local key = hash .. (':close:%d'):format(msg.from.id)
+ local key = hash .. (':close:%d'):format(msg.from.user.id)
local second_tap = red:get(key)
if second_tap == null then
red:setex(key, 3600*24, 'x')
@@ -205,12 +205,12 @@ function _M:onCallbackQuery(blocks)
else
local chats_reached = red:hgetall(hash)
for user_id, message_id in pairs(chats_reached) do
- if tonumber(user_id) ~= msg.from.id then
+ if tonumber(user_id) ~= msg.from.user.id then
api:deleteMessages(user_id, { [1] = message_id, [2] = (tonumber(message_id) - 1) })
end
end
local markup = {inline_keyboard={{{text = i18n("(issue closed by you)"), callback_data = "issueclosed"}}}}
- api:editMessageReplyMarkup(msg.from.id, msg.message_id, nil, markup)
+ api:editMessageReplyMarkup(msg.from.user.id, msg.message_id, nil, markup)
end
end
end
diff --git a/lua/groupbutler/plugins/rules.lua b/lua/groupbutler/plugins/rules.lua
index bfb80d3be..7ee5ead1c 100644
--- a/lua/groupbutler/plugins/rules.lua
+++ b/lua/groupbutler/plugins/rules.lua
@@ -28,21 +28,21 @@ function _M:onTextMessage(blocks)
local api_err = self.api_err
local u = self.u
- if msg.chat.type == 'private' then
+ if msg.from.chat.type == 'private' then
if blocks[1] == 'start' then
- msg.chat.id = tonumber(blocks[2])
+ msg.from.chat.id = tonumber(blocks[2])
- local res = api:getChat(msg.chat.id)
+ local res = api:getChat(msg.from.chat.id)
if not res then
- api:sendMessage(msg.from.id, i18n("🚫 Unknown or non-existent group"))
+ api:sendMessage(msg.from.user.id, i18n("🚫 Unknown or non-existent group"))
return
end
-- Private chats have no username
local private = not res.username
- res = api:getChatMember(msg.chat.id, msg.from.id)
+ res = api:getChatMember(msg.from.chat.id, msg.from.user.id)
if not res or (res.status == 'left' or res.status == 'kicked') and private then
- api:sendMessage(msg.from.id, i18n("🚷 You are not a member of this chat. " ..
+ api:sendMessage(msg.from.user.id, i18n("🚷 You are not a member of this chat. " ..
"You can't read the rules of a private group."))
return
end
@@ -51,22 +51,22 @@ function _M:onTextMessage(blocks)
end
end
- local hash = 'chat:'..msg.chat.id..':info'
+ local hash = 'chat:'..msg.from.chat.id..':info'
if blocks[1] == 'rules' or blocks[1] == 'start' then
- local rules = u:getRules(msg.chat.id)
+ local rules = u:getRules(msg.from.chat.id)
local reply_markup
reply_markup, rules = u:reply_markup_from_text(rules)
local link_preview = rules:find('telegra%.ph/') == nil
- if msg.chat.type == 'private' or (not send_in_group(self, msg.chat.id) and not msg:is_from_admin()) then
- api:sendMessage(msg.from.id, rules, "Markdown", link_preview, nil, nil, reply_markup)
+ if msg.from.chat.type == 'private' or (not send_in_group(self, msg.from.chat.id) and not msg.from:isAdmin()) then
+ api:sendMessage(msg.from.user.id, rules, "Markdown", link_preview, nil, nil, reply_markup)
else
msg:send_reply(rules, "Markdown", link_preview, nil, nil, reply_markup)
end
end
- if not u:is_allowed('texts', msg.chat.id, msg.from) then return end
+ if not msg.from:isAdmin() then return end
if blocks[1] == 'setrules' then
local rules = blocks[2]
@@ -87,11 +87,11 @@ function _M:onTextMessage(blocks)
--set the new rules
local ok, err = msg:send_reply(test_text, "Markdown", nil, nil, reply_markup)
if not ok then
- api:sendMessage(msg.chat.id, api_err:trans(err), "Markdown")
+ api:sendMessage(msg.from.chat.id, api_err:trans(err), "Markdown")
else
red:hset(hash, 'rules', rules)
local id = ok.message_id
- api:editMessageText(msg.chat.id, id, nil, i18n("New rules *saved successfully*!"), "Markdown")
+ api:editMessageText(msg.from.chat.id, id, nil, i18n("New rules *saved successfully*!"), "Markdown")
end
end
end
diff --git a/lua/groupbutler/plugins/service.lua b/lua/groupbutler/plugins/service.lua
index 486163f9a..fd226cd7a 100644
--- a/lua/groupbutler/plugins/service.lua
+++ b/lua/groupbutler/plugins/service.lua
@@ -23,43 +23,36 @@ function _M:onTextMessage(blocks)
if not msg.service then return end
if blocks[1] == "new_chat_member" then
- red:sadd(string.format("chat:%d:members", msg.chat.id), msg.new_chat_member.id)
+ red:sadd(string.format("chat:%d:members", msg.from.chat.id), msg.new_chat_member.id)
end
if blocks[1] == "left_chat_member" then
- red:srem(string.format("chat:%d:members", msg.chat.id), msg.left_chat_member.id)
+ red:srem(string.format("chat:%d:members", msg.from.chat.id), msg.left_chat_member.id)
end
if blocks[1] == 'new_chat_member'
or blocks[1] == 'left_chat_member' then
- local status = red:hget(('chat:%d:settings'):format(msg.chat.id), 'Clean_service_msg')
+ local status = red:hget(('chat:%d:settings'):format(msg.from.chat.id), 'Clean_service_msg')
if status == null then status = config.chat_settings.settings.Clean_service_msg end
if status == 'on' then
- api:deleteMessage(msg.chat.id, msg.message_id)
+ api:deleteMessage(msg.from.chat.id, msg.message_id)
end
return true
end
if blocks[1] == 'new_chat_member:bot' or blocks[1] == 'migrate_from_chat_id' then
- -- set the language
- --[[locale.language = red:get(string.format('lang:%d', msg.from.id)) or config.lang
- if not config.available_languages[locale.language] then
- locale.language = 'en'
- end]]
- if u:is_blocked_global(msg.from.id) then
- api:sendMessage(msg.chat.id, i18n("_You (user ID: %d) are in the blocked list_"):format(msg.from.id), "Markdown")
- api:leaveChat(msg.chat.id)
+ if u:is_blocked_global(msg.from.user.id) then
+ api:sendMessage(msg.from.chat.id, i18n("_You (user ID: %d) are in the blocked list_"):format(msg.from.user.id),
+ "Markdown")
+ api:leaveChat(msg.from.chat.id)
return
end
- if config.bot_settings.admin_mode and not u:is_superadmin(msg.from.id) then
- api:sendMessage(msg.chat.id, i18n("_Admin mode is on: only the bot admin can add me to a new group_"), "Markdown")
- api:leaveChat(msg.chat.id)
+ if config.bot_settings.admin_mode and not u:is_superadmin(msg.from.user.id) then
+ api:sendMessage(msg.from.chat.id, i18n("_Admin mode is on: only the bot admin can add me to a new group_"),
+ "Markdown")
+ api:leaveChat(msg.from.chat.id)
return
end
- -- save language
- --[[if locale.language then
- red:set(string.format('lang:%d', msg.chat.id), locale.language)
- end]]
- u:initGroup(msg.chat.id)
+ u:initGroup(msg.from.chat)
-- send manuals
local text
if blocks[1] == 'new_chat_member:bot' then
@@ -69,23 +62,9 @@ function _M:onTextMessage(blocks)
else
text = i18n("Yay! This group has been upgraded. You are great! Now I can work properly :)\n")
end
- --[[if not u:is_admin(msg.chat.id, bot.id) then
- if u:is_owner(msg.chat.id, msg.from.id) then
- text = text .. i18n("Hmm… apparently I'm not an administrator. "
- .. "I can be more useful if you make me an admin. "
- .. "See [here](https://telegram.me/GroupButler_ch/104) how to do it.\n")
- else
- text = text .. i18n("Hmm… apparently I'm not an administrator. "
- .. "I can be more useful if I'm an admin. Ask a creator to make me an admin. "
- .. "If they don't know how, there is a good [guide](https://telegram.me/GroupButler_ch/104).\n")
- end
- end
- text = text .. i18n("I can do a lot of cool things. To discover about them, "
- -- TODO: old link, update it
- .. "watch this [video-tutorial](https://youtu.be/uqNumbcUyzs).") ]]
- api:sendMessage(msg.chat.id, text, "Markdown")
+ api:sendMessage(msg.from.chat.id, text, "Markdown")
elseif blocks[1] == 'left_chat_member:bot' then
- u:remGroup(msg.chat.id)
+ u:remGroup(msg.from.chat.id)
end
u:logEvent(blocks[1], msg)
diff --git a/lua/groupbutler/plugins/setlang.lua b/lua/groupbutler/plugins/setlang.lua
index 285f7b051..ee102c0ff 100644
--- a/lua/groupbutler/plugins/setlang.lua
+++ b/lua/groupbutler/plugins/setlang.lua
@@ -25,12 +25,12 @@ end
function _M:onTextMessage()
local api = self.api
local msg = self.message
- local u = self.u
local i18n = self.i18n
- if msg.chat.type == 'private' or (msg.chat.id < 0 and u:is_allowed('config', msg.chat.id, msg.from)) then
+ if msg.from.chat.type == "private"
+ or msg.from:can("can_change_info") then
local keyboard = doKeyboard_lang()
- api:sendMessage(msg.chat.id, i18n("*List of available languages*:"), "Markdown", nil, nil, nil, keyboard)
+ api:sendMessage(msg.from.chat.id, i18n("*List of available languages*:"), "Markdown", nil, nil, nil, keyboard)
end
end
@@ -40,27 +40,30 @@ function _M:onCallbackQuery(blocks)
local red = self.red
local i18n = self.i18n
- if msg.chat.type ~= 'private' and not msg:is_from_admin() then
- api:answerCallbackQuery(msg.cb_id, i18n("You are not an admin"))
- else
- if blocks[1] == 'selectlang' then
- local keyboard = doKeyboard_lang()
- api:editMessageText(msg.chat.id, msg.message_id, nil, i18n("*List of available languages*:"), "Markdown", nil,
- keyboard)
- else
- i18n:setLanguage(blocks[1])
- red:set('lang:'..msg.chat.id, i18n:getLanguage())
- if (blocks[1] == 'ar_SA' or blocks[1] == 'fa_IR') and msg.chat.type ~= 'private' then
- red:hset('chat:'..msg.chat.id..':char', 'Arab', 'allowed')
- red:hset('chat:'..msg.chat.id..':char', 'Rtl', 'allowed')
- end
- -- TRANSLATORS: replace 'English' with the name of your language
- api:editMessageText(msg.chat.id, msg.message_id, nil, i18n("English language is *set*") ..
-i18n([[.
+ if msg.from.chat.type ~= "private"
+ and not msg.from:isAdmin() then
+ api:answerCallbackQuery(msg.cb_id, i18n("Sorry, you don't have permission to change settings"))
+ return
+ end
+
+ if blocks[1] == "selectlang" then
+ local keyboard = doKeyboard_lang()
+ api:editMessageText(msg.from.chat.id, msg.message_id, nil, i18n("*List of available languages*:"), "Markdown", nil,
+ keyboard)
+ return
+ end
+
+ i18n:setLanguage(blocks[1])
+ red:set("lang:"..msg.from.chat.id, i18n:getLanguage())
+ if msg.from.chat.type ~= "private"
+ and (blocks[1] == "ar_SA" or blocks[1] == "fa_IR") then
+ red:hset("chat:"..msg.from.chat.id..":char", "Arab", "allowed")
+ red:hset("chat:"..msg.from.chat.id..":char", "Rtl", "allowed")
+ end
+ -- TRANSLATORS: replace 'English' with the name of your language
+ api:editMessageText(msg.from.chat.id, msg.message_id, nil, i18n("English language is *set*")..i18n([[.
Please note that translators are volunteers, and this localization _may be incomplete_. You can help improve translations on our [Crowdin Project](https://crowdin.com/project/group-butler).
]]), "Markdown")
- end
- end
end
_M.triggers = {
diff --git a/lua/groupbutler/plugins/users.lua b/lua/groupbutler/plugins/users.lua
index 0ebf008b7..989520347 100644
--- a/lua/groupbutler/plugins/users.lua
+++ b/lua/groupbutler/plugins/users.lua
@@ -1,4 +1,6 @@
local config = require "groupbutler.config"
+local User = require("groupbutler.user")
+local Chat = require("groupbutler.chat")
local _M = {}
@@ -11,6 +13,11 @@ function _M:new(update_obj)
return plugin_obj
end
+local function set_default(t, d)
+ local mt = {__index = function() return d end}
+ setmetatable(t, mt)
+end
+
local function permissions(self)
local i18n = self.i18n
return {
@@ -33,26 +40,6 @@ local function do_keyboard_cache(self, chat_id)
return keyboard
end
-local function get_time_remaining(seconds)
- local final = ''
- local hours = math.floor(seconds/3600)
- seconds = seconds - (hours*60*60)
- local min = math.floor(seconds/60)
- seconds = seconds - (min*60)
-
- if hours and hours > 0 then
- final = final..hours..'h '
- end
- if min and min > 0 then
- final = final..min..'m '
- end
- if seconds and seconds > 0 then
- final = final..seconds..'s'
- end
-
- return final
-end
-
local function do_keyboard_userinfo(self, user_id)
local i18n = self.i18n
local keyboard = {
@@ -81,52 +68,44 @@ end
function _M:onTextMessage(blocks)
local api = self.api
local msg = self.message
- local red = self.red
+ local db = self.db
local i18n = self.i18n
local u = self.u
if blocks[1] == 'id' then --in private: send user id
- if msg.chat.id > 0 and msg.chat.type == 'private' then
- api:sendMessage(msg.chat.id, string.format(i18n('Your ID is `%d`'), msg.from.id), "Markdown")
+ if msg.from.chat.id > 0 and msg.from.chat.type == 'private' then
+ api:sendMessage(msg.from.chat.id, string.format(i18n('Your ID is `%d`'), msg.from.user.id), "Markdown")
end
end
- if msg.chat.type == 'private' then return end
+ if msg.from.chat.type == 'private' then return end
if blocks[1] == 'id' then --in groups: send chat ID
- if msg.chat.id < 0 and msg:is_from_admin() then
- api:sendMessage(msg.chat.id, string.format('`%d`', msg.chat.id), "Markdown")
+ if msg.from.chat.id < 0 and msg.from:isAdmin() then
+ api:sendMessage(msg.from.chat.id, string.format('`%d`', msg.from.chat.id), "Markdown")
end
end
if blocks[1] == 'adminlist' then
- local adminlist = u:getAdminlist(msg.chat.id)
- if not msg:is_from_admin() then
- api:sendMessage(msg.from.id, adminlist, 'html', true)
+ local adminlist = u:getAdminlist(msg.from.chat)
+ if not msg.from:isAdmin() then
+ api:sendMessage(msg.from.user.id, adminlist, 'html', true)
else
msg:send_reply(adminlist, 'html', true)
end
end
if blocks[1] == 'status' then
- if (not blocks[2] and not msg.reply) or not msg:is_from_admin() then
+ if (not blocks[2] and not msg.reply) or not msg.from:isAdmin() then
return
end
- local user_id, error_tr_id = u:get_user_id(msg, blocks)
- if not user_id then
- msg:send_reply(error_tr_id, "Markdown")
- return
- end
- local res = api:getChatMember(msg.chat.id, user_id)
-
- if not res then
- msg:send_reply(i18n("That user has nothing to do with this chat"))
+ local member, err = msg:getTargetMember(blocks)
+ if not member then
+ msg:send_reply(err, "Markdown")
return
end
- local status = res.status
- local name = u:getname_final(res.user)
local statuses = {
kicked = i18n("%s is banned from this group"),
left = i18n("%s left the group or has been kicked and unbanned"),
@@ -135,24 +114,25 @@ function _M:onTextMessage(blocks)
unknown = i18n("%s has nothing to do with this chat"),
member = i18n("%s is a chat member"),
restricted = i18n("%s is a restricted")
- }
+ } set_default(statuses, statuses.unknown)
+
local denied_permissions = {}
for permission, str in pairs(permissions(self)) do
- if res[permission] ~= nil and res[permission] == false then
+ if member[permission] ~= nil and member[permission] == false then
table.insert(denied_permissions, str)
end
end
- local text = statuses[status]:format(name)
+ local text = statuses[member.status]:format(member.user:getLink())
if next(denied_permissions) then
text = text..i18n('\nRestrictions: %s'):format(table.concat(denied_permissions, ', '))
end
msg:send_reply(text, 'html')
end
- if blocks[1] == 'user' then
- if not msg:is_from_admin() then return end
+ if blocks[1] == 'user' then
+ if not msg.from:isAdmin() then return end
if not msg.reply
and (not blocks[2] or (not blocks[2]:match('@[%w_]+$') and not blocks[2]:match('%d+$')
and not msg.mention_id)) then
@@ -160,92 +140,70 @@ function _M:onTextMessage(blocks)
return
end
- ------------------ get user_id --------------------------
- local user_id, err = u:get_user_id(msg, blocks)
-
- if not user_id then
+ local member, err = msg:getTargetMember(blocks)
+ if not member then
msg:send_reply(err, "Markdown")
return
end
- -----------------------------------------------------------------------------
-
- local keyboard = do_keyboard_userinfo(self, user_id)
- local text = get_userinfo(self, user_id, msg.chat.id)
+ local keyboard = do_keyboard_userinfo(self, member.user.id)
- api:sendMessage(msg.chat.id, text, "Markdown", nil, nil, nil, keyboard)
+ local text = get_userinfo(self, member.user.id, msg.from.chat.id)
+ api:sendMessage(msg.from.chat.id, text, "Markdown", nil, nil, nil, keyboard)
end
+
if blocks[1] == 'cache' then
- if not msg:is_from_admin() then return end
- local hash = 'cache:chat:'..msg.chat.id..':admins'
- local seconds = red:ttl(hash)
- local cached_admins = red:scard(hash)
- local text = i18n("📌 Status: `CACHED`\n⌛ ️Remaining: `%s`\n👥 Admins cached: `%d`")
- :format(get_time_remaining(tonumber(seconds)), cached_admins)
- local keyboard = do_keyboard_cache(self, msg.chat.id)
- api:sendMessage(msg.chat.id, text, "Markdown", nil, nil, nil, keyboard)
+ if not msg.from:isAdmin() then return end
+ local text = i18n("👥 Admins cached: %d
"):format(db:getChatAdministratorsCount(msg.from.chat))
+ local keyboard = do_keyboard_cache(self, msg.from.chat.id)
+ api:sendMessage(msg.from.chat.id, text, "html", nil, nil, nil, keyboard)
end
- if blocks[1] == 'msglink' then
- if not msg.reply or not msg.chat.username then return end
+ if blocks[1] == 'msglink' then
+ if not msg.reply or not msg.from.chat.username then return end
local text = string.format('[%s](https://telegram.me/%s/%d)',
- i18n("Message N° %d"):format(msg.reply.message_id), msg.chat.username, msg.reply.message_id)
- if not u:is_silentmode_on(msg.chat.id) or msg:is_from_admin() then
+ i18n("Message N° %d"):format(msg.reply.message_id), msg.from.chat.username, msg.reply.message_id)
+ if not u:is_silentmode_on(msg.from.chat.id) or msg.from:isAdmin() then
msg.reply:send_reply(text, "Markdown")
else
- api:sendMessage(msg.from.id, text, "Markdown")
+ api:sendMessage(msg.from.user.id, text, "Markdown")
end
end
- if blocks[1] == 'leave' and msg:is_from_admin() then
- -- u:remGroup(msg.chat.id)
- api:leaveChat(msg.chat.id)
+
+ if blocks[1] == 'leave' and msg.from:isAdmin() then
+ -- u:remGroup(msg.from.chat.id)
+ api:leaveChat(msg.from.chat.id)
end
end
function _M:onCallbackQuery(blocks)
local api = self.api
local msg = self.message
- local red = self.red
local db = self.db
local i18n = self.i18n
local u = self.u
- if not msg:is_from_admin() then
+ if not msg.from:isAdmin() then
api:answerCallbackQuery(msg.cb_id, i18n("You are not allowed to use this button"))
return
end
if blocks[1] == 'remwarns' then
- db:forgetUserWarns(msg.chat.id, blocks[2])
-
- local name = u:getname_final(msg.from)
- local res = api:getChatMember(msg.chat.id, blocks[2])
- local text = i18n("The number of warnings received by this user has been reset, by %s"):format(name)
- api:editMessageText(msg.chat.id, msg.message_id, nil, text:format(name), 'html')
- u:logEvent('nowarn', msg, {
- admin = name,
- user = u:getname_final(res.user),
+ db:forgetUserWarns(msg.from.chat.id, blocks[2])
+ local admin = msg.from.user
+ local target = User:new({id = blocks[2]}, self)
+ local text = i18n("The number of warnings received by this user has been reset, by %s"):format(admin:getLink())
+ api:editMessageText(msg.from.chat.id, msg.message_id, nil, text, "html")
+ u:logEvent("nowarn", msg, {
+ admin = admin,
+ user = target,
user_id = blocks[2]
})
end
- if blocks[1] == 'recache' and msg:is_from_admin() then
- local missing_sec = tonumber(red:ttl('cache:chat:'..msg.target_id..':admins') or 0)
- local wait = 600
- if config.bot_settings.cache_time.adminlist - missing_sec < wait then
- local seconds_to_wait = wait - (config.bot_settings.cache_time.adminlist - missing_sec)
- api:answerCallbackQuery(msg.cb_id,i18n(
- "The adminlist has just been updated. You must wait 10 minutes from the last refresh (wait %d seconds)"
- ):format(seconds_to_wait), true)
- else
- red:del('cache:chat:'..msg.target_id..':admins')
- u:cache_adminlist(msg.target_id)
- local cached_admins = red:smembers('cache:chat:'..msg.target_id..':admins')
- local time = get_time_remaining(config.bot_settings.cache_time.adminlist)
- local text = i18n("📌 Status: `CACHED`\n⌛ ️Remaining: `%s`\n👥 Admins cached: `%d`")
- :format(time, #cached_admins)
- api:answerCallbackQuery(msg.cb_id, i18n("✅ Updated. Next update in %s"):format(time))
- api:editMessageText(msg.chat.id, msg.message_id, nil, text, "Markdown", nil, do_keyboard_cache(self, msg.target_id))
- end
+
+ if blocks[1] == 'recache' and msg.from:isAdmin() then
+ u:cache_adminlist(Chat:new({id=msg.target_id}, self))
+ api:answerCallbackQuery(msg.cb_id, i18n("✅ The admin list will be updated soon"))
end
end
diff --git a/lua/groupbutler/plugins/warn.lua b/lua/groupbutler/plugins/warn.lua
index bf92c02f0..5c60a5352 100644
--- a/lua/groupbutler/plugins/warn.lua
+++ b/lua/groupbutler/plugins/warn.lua
@@ -29,14 +29,14 @@ function _M:onTextMessage(blocks)
local i18n = self.i18n
local u = self.u
- if msg.chat.type == 'private'
- or (msg.chat.type ~= 'private' and not u:is_allowed('hammer', msg.chat.id, msg.from)) then
+ if msg.from.chat.type == "private"
+ or not msg.from:isAdmin() then
return
end
if blocks[1] == 'warnmax' then
local new, default, text, key
- local hash = 'chat:'..msg.chat.id..':warnsettings'
+ local hash = 'chat:'..msg.from.chat.id..':warnsettings'
if blocks[2] == 'media' then
new = blocks[3]
default = 2
@@ -64,7 +64,7 @@ function _M:onTextMessage(blocks)
{{{text = i18n('Yes'), callback_data = 'cleanwarns:yes'}, {text = i18n('No'), callback_data = 'cleanwarns:no'}}}
}
- api:sendMessage(msg.chat.id,
+ api:sendMessage(msg.from.chat.id,
i18n('Do you want to continue and reset *all* the warnings received by *all* the users of the group?'),
"Markdown", nil, nil, nil, reply_markup)
@@ -72,85 +72,88 @@ function _M:onTextMessage(blocks)
end
--do not reply when...
- local user_id, err_msg = u:get_user_id(msg, blocks)
- if not user_id then
- msg:send_reply(err_msg, "Markdown")
- return
+ local admin = msg.from
+ local target
+ do
+ local err
+ target, err = msg:getTargetMember(blocks)
+ if not target then
+ msg:send_reply(err, "Markdown")
+ return
+ end
end
- if tonumber(user_id) == bot.id then return end
+ if tonumber(target.user.id) == bot.id then return end
if blocks[1] == 'nowarn' then
- db:forgetUserWarns(msg.chat.id, msg.reply.from.id)
- local admin = u:getname_final(msg.from)
- local user = u:getname_final(msg.reply.from)
- local text = i18n("Done! %s has been forgiven."):format(user)
+ db:forgetUserWarns(msg.from.chat.id, msg.reply.from.user.id)
+ local text = i18n("Done! %s has been forgiven."):format(target.user:getLink())
msg:send_reply(text, 'html')
u:logEvent('nowarn', msg, {
- admin = admin,
- user = user,
- user_id = msg.reply.from.id
+ admin = admin.user,
+ user = target.user,
+ user_id = target.user.id
})
+ return
end
- if u:is_admin(msg.chat.id, user_id) then return end
+ if target:isAdmin() then return end
if blocks[1] == 'warn' or blocks[1] == 'sw' then
- -- Get the user that was targeted, again, but get the name this time
- local admin_name, target_name = u:getnames_complete(msg)
-
- local hash = 'chat:'..msg.chat.id..':warns'
- local num = tonumber(red:hincrby(hash, user_id, 1)) --add one warn
- local nmax = tonumber(red:hget('chat:'..msg.chat.id..':warnsettings', 'max')) or 3 --get the max num of warnings
+ local hash = 'chat:'..msg.from.chat.id..':warns'
+ local num = tonumber(red:hincrby(hash, target.user.id, 1)) --add one warn
+ local nmax = tonumber(red:hget('chat:'..msg.from.chat.id..':warnsettings', 'max')) or 3 --get the max num of warnings
local text, res, err, hammer_log
if num >= nmax then
- local type = red:hget('chat:'..msg.chat.id..':warnsettings', 'type')
+ local type = red:hget('chat:'..msg.from.chat.id..':warnsettings', 'type')
if type == null then type = 'kick' end
--try to kick/ban
text = i18n("%s %s: reached the max number of warnings (%d/%d
)")
if type == 'ban' then
hammer_log = i18n('banned')
- text = text:format(target_name, hammer_log, num, nmax)
- res, err = u:banUser(msg.chat.id, user_id)
+ text = text:format(target.user:getLink(), hammer_log, num, nmax)
+ res, err = target:ban()
elseif type == 'kick' then --kick
hammer_log = i18n('kicked')
- text = text:format(target_name, hammer_log, num, nmax)
- res, err = u:kickUser(msg.chat.id, user_id)
+ text = text:format(target.user:getLink(), hammer_log, num, nmax)
+ res, err = target:kick()
elseif type == 'mute' then --kick
hammer_log = i18n('muted')
- text = text:format(target_name, hammer_log, num, nmax)
- res, err = u:muteUser(msg.chat.id, user_id)
+ text = text:format(target.user:getLink(), hammer_log, num, nmax)
+ res, err = target:mute()
end
--if kick/ban fails, send the motivation
if not res then
- if num > nmax then red:hset(hash, user_id, nmax) end --avoid to have a number of warnings bigger than the max
+ if num > nmax then red:hset(hash, target.user.id, nmax) end --avoid to have a number of warnings bigger than the max
text = err
else
- db:forgetUserWarns(msg.chat.id, user_id)
+ db:forgetUserWarns(msg.from.chat.id, target.user.id)
end
--if the user reached the max num of warns, kick and send message
msg:send_reply(text, 'html')
u:logEvent('warn', msg, {
motivation = blocks[2],
- admin = admin_name,
- user = target_name,
- user_id = user_id,
+ admin = admin.user,
+ user = target.user,
+ user_id = target.user.id,
hammered = hammer_log,
warns = num,
warnmax = nmax
})
else
- text = i18n("%s has been warned (%d/%d
)"):format(target_name, num, nmax)
- local keyboard = doKeyboard_warn(self, user_id)
- if blocks[1] ~= 'sw' then api:sendMessage(msg.chat.id, text, 'html', true, nil, nil, keyboard) end
+ if blocks[1] ~= 'sw' then
+ text = i18n("%s has been warned (%d/%d
)"):format(target.user:getLink(), num, nmax)
+ local keyboard = doKeyboard_warn(self, target.user.id)
+ api:sendMessage(msg.from.chat.id, text, 'html', true, nil, nil, keyboard)
+ end
u:logEvent('warn', msg, {
motivation = blocks[2],
warns = num,
warnmax = nmax,
- admin = admin_name,
- user = target_name,
- user_id = user_id
+ admin = admin.user,
+ user = target.user,
+ user_id = target.user.id
})
end
end
@@ -161,36 +164,40 @@ function _M:onCallbackQuery(blocks)
local msg = self.message
local red = self.red
local i18n = self.i18n
- local u = self.u
- if not u:is_allowed('hammer', msg.chat.id, msg.from) then
- api:answerCallbackQuery(msg.cb_id, i18n("You are not allowed to use this button")) return
+ if msg.from.chat.type == "private"
+ or not msg.from:isAdmin() then
+ api:answerCallbackQuery(msg.cb_id, i18n("You are not allowed to use this button"))
+ return
end
+ local admin = msg.from.user
+
if blocks[1] == 'removewarn' then
local user_id = blocks[2]
- local num = tonumber(red:hincrby('chat:'..msg.chat.id..':warns', user_id, -1)) --add one warn
+ local num = tonumber(red:hincrby('chat:'..msg.from.chat.id..':warns', user_id, -1)) --add one warn
local text, nmax
if num < 0 then
text = i18n("The number of warnings received by this user is already zero")
- red:hincrby('chat:'..msg.chat.id..':warns', user_id, 1) --restore the previouvs number
+ red:hincrby('chat:'..msg.from.chat.id..':warns', user_id, 1) --restore the previouvs number
else
- nmax = tonumber(red:hget('chat:'..msg.chat.id..':warnsettings', 'max')) or 3 --get the max num of warnings
+ nmax = tonumber(red:hget('chat:'..msg.from.chat.id..':warnsettings', 'max')) or 3 --get the max num of warnings
text = i18n("Warn removed! (%d/%d)"):format(num, nmax)
end
- text = text .. i18n("\n(Admin: %s)"):format(u:getname_final(msg.from))
- api:editMessageText(msg.chat.id, msg.message_id, nil, text, 'html')
+ text = text .. i18n("\n(Admin: %s)"):format(admin:getLink())
+ api:editMessageText(msg.from.chat.id, msg.message_id, nil, text, 'html')
end
+
if blocks[1] == 'cleanwarns' then
if blocks[2] == 'yes' then
- red:del('chat:'..msg.chat.id..':warns')
- red:del('chat:'..msg.chat.id..':mediawarn')
- red:del('chat:'..msg.chat.id..':spamwarns')
- api:editMessageText(msg.chat.id, msg.message_id, nil,
- i18n('Done. All the warnings of this group have been erased by %s'):format(u:getname_final(msg.from)), 'html')
+ red:del('chat:'..msg.from.chat.id..':warns')
+ red:del('chat:'..msg.from.chat.id..':mediawarn')
+ red:del('chat:'..msg.from.chat.id..':spamwarns')
+ api:editMessageText(msg.from.chat.id, msg.message_id, nil,
+ i18n('Done. All the warnings of this group have been erased by %s'):format(admin:getLink()), 'html')
else
- api:editMessageText(msg.chat.id, msg.message_id, nil, i18n('_Action aborted_'), "Markdown")
+ api:editMessageText(msg.from.chat.id, msg.message_id, nil, i18n('_Action aborted_'), "Markdown")
end
end
end
diff --git a/lua/groupbutler/plugins/welcome.lua b/lua/groupbutler/plugins/welcome.lua
index b9dfa8089..c089fa9e1 100644
--- a/lua/groupbutler/plugins/welcome.lua
+++ b/lua/groupbutler/plugins/welcome.lua
@@ -15,51 +15,39 @@ end
local function ban_bots(self, msg)
local db = self.db
- local u = self.u
-
-- ignore if added by an admin or new member joined by link or the setting is disabled
- if msg.from.id == msg.new_chat_member.id
- or msg:is_from_admin()
- or not db:get_chat_setting(msg.chat.id, 'Antibot') then
+ if msg.from.user.id == msg.new_chat_member.id
+ or msg.from:isAdmin()
+ or not db:get_chat_setting(msg.from.chat.id, 'Antibot') then
return
end
-
- local users = msg.new_chat_members
+ local members = msg.new_chat_members
local n = 0 --bots banned
- for i = 1, #users do
- if users[i].is_bot == true then
- u:banUser(msg.chat.id, users[i].id)
+ for i = 1, #members do
+ if members[i].user.is_bot then
+ members[i]:ban()
n = n + 1
end
end
- if n == #users then
+ if n == #members then
--if all the new members added are bots then don't send a welcome message
return true
end
end
--- local permissions =
--- {'can_send_messages', 'can_send_media_messages', 'can_send_other_messages', 'can_add_web_page_previews'}
-
-local function apply_default_permissions(self, chat_id, users)
+local function apply_default_permissions(self, msg)
local api = self.api
local red = self.red
+ local members = msg.new_chat_members
- local hash = ('chat:%d:defpermissions'):format(chat_id)
+ local hash = ("chat:%d:defpermissions"):format(msg.from.chat.id)
local def_permissions = red:array_to_hash(red:hgetall(hash))
if next(def_permissions) then
- --for i=1, #permissions do
- --if not def_permissions[permissions[i]] then
- --def_permissions[permissions[i]] = config.chat_settings.defpermissions[permissions[i]]
- --end
- --end
-
- for i=1, #users do
- local res = api:getChatMember(chat_id, users[i].id)
- if res.status ~= 'restricted' then
- def_permissions.chat_id = chat_id
- def_permissions.user_id = users[i].id
+ for i=1, #members do
+ if members[i].status == "member" then
+ def_permissions.chat_id = msg.from.chat.id
+ def_permissions.user_id = members[i].user.id
api:restrictChatMember(def_permissions)
end
end
@@ -76,11 +64,11 @@ local function get_reply_markup(self, msg, text)
reply_markup, new_text = u:reply_markup_from_text(u:replaceholders(text, msg))
end
- if db:get_chat_setting(msg.chat.id, "Welbut") then
+ if db:get_chat_setting(msg.from.chat.id, "Welbut") then
if not reply_markup then
reply_markup = api_u.InlineKeyboardMarkup:new()
end
- reply_markup:row({text = i18n("Read the rules"), url = u:deeplink_constructor(msg.chat.id, "rules")})
+ reply_markup:row({text = i18n("Read the rules"), url = u:deeplink_constructor(msg.from.chat.id, "rules")})
end
return reply_markup, new_text
@@ -93,11 +81,11 @@ local function send_welcome(self, msg)
local u = self.u
local db = self.db
- if not db:get_chat_setting(msg.chat.id, 'Welcome') then
+ if not db:get_chat_setting(msg.from.chat.id, 'Welcome') then
return
end
- local hash = 'chat:'..msg.chat.id..':welcome'
+ local hash = 'chat:'..msg.from.chat.id..':welcome'
local welcome_type = red:hget(hash, "type")
local content = red:hget(hash, 'content')
if welcome_type == "no" or content == "no" -- TODO: database migration no -> null
@@ -111,7 +99,7 @@ local function send_welcome(self, msg)
or welcome_type == "text" then
local reply_markup, text = get_reply_markup(self, msg, content)
local link_preview = text:find('telegra%.ph/') == nil
- ok, err = api:sendMessage(msg.chat.id, text, "Markdown", link_preview, nil, nil, reply_markup)
+ ok, err = api:sendMessage(msg.from.chat.id, text, "Markdown", link_preview, nil, nil, reply_markup)
end
if welcome_type == "media" then
local caption = red:hget(hash, "caption")
@@ -119,20 +107,20 @@ local function send_welcome(self, msg)
caption = nil
end
local reply_markup, text = get_reply_markup(self, msg, caption)
- ok, err = api:sendDocument(msg.chat.id, content, text, nil, nil, reply_markup)
+ ok, err = api:sendDocument(msg.from.chat.id, content, text, nil, nil, reply_markup)
end
if not ok and err.description:match("have no rights to send a message") then
- u:remGroup(msg.chat.id)
- api:leaveChat(msg.chat.id)
+ u:remGroup(msg.from.chat.id)
+ api:leaveChat(msg.from.chat.id)
return
end
- if ok and db:get_chat_setting(msg.chat.id, "Weldelchain") then
- local key = ('chat:%d:lastwelcome'):format(msg.chat.id) -- get the id of the last sent welcome message
+ if ok and db:get_chat_setting(msg.from.chat.id, "Weldelchain") then
+ local key = ('chat:%d:lastwelcome'):format(msg.from.chat.id) -- get the id of the last sent welcome message
local message_id = red:get(key)
if message_id ~= null then
- api:deleteMessage(msg.chat.id, message_id)
+ api:deleteMessage(msg.from.chat.id, message_id)
end
red:setex(key, 259200, ok.message_id) --set the new message id to delete
end
@@ -148,7 +136,10 @@ function _M:onTextMessage(blocks)
local u = self.u
if blocks[1] == 'welcome' then
- if msg.chat.type == 'private' or not u:is_allowed('texts', msg.chat.id, msg.from) then return end
+ if msg.from.chat.type == "private"
+ or not msg.from:can("can_change_info") then
+ return
+ end
local input = blocks[2]
if not input and not msg.reply then
@@ -156,7 +147,7 @@ function _M:onTextMessage(blocks)
return
end
- local hash = 'chat:'..msg.chat.id..':welcome'
+ local hash = 'chat:'..msg.from.chat.id..':welcome'
if not input and msg.reply then
local replied_to = msg.reply:type()
@@ -175,7 +166,7 @@ function _M:onTextMessage(blocks)
red:hdel(hash, 'caption') --remove the caption key if the new media doesn't have a caption
end
-- turn on the welcome message in the group settings
- db:set_chat_setting(msg.chat.id, "Welcome", true)
+ db:set_chat_setting(msg.from.chat.id, "Welcome", true)
msg:send_reply(i18n("A form of media has been set as the welcome message: `%s`"):format(replied_to), "Markdown")
else
msg:send_reply(i18n("Reply to a `sticker` or a `gif` to set them as the *welcome message*"), "Markdown")
@@ -183,7 +174,7 @@ function _M:onTextMessage(blocks)
else
local reply_markup, new_text = u:reply_markup_from_text(input)
- new_text = new_text:gsub('$rules', u:deeplink_constructor(msg.chat.id, 'rules'))
+ new_text = new_text:gsub('$rules', u:deeplink_constructor(msg.from.chat.id, 'rules'))
red:hset(hash, 'type', 'custom')
red:hset(hash, 'content', input)
@@ -192,31 +183,32 @@ function _M:onTextMessage(blocks)
if not ok then
red:hset(hash, 'type', 'no') --if wrong markdown, remove 'custom' again
red:hset(hash, 'content', 'no')
- api:sendMessage(msg.chat.id, api_err:trans(err), "Markdown")
+ api:sendMessage(msg.from.chat.id, api_err:trans(err), "Markdown")
else
-- turn on the welcome message in the group settings
- db:set_chat_setting(msg.chat.id, "Welcome", true)
+ db:set_chat_setting(msg.from.chat.id, "Welcome", true)
local id = ok.message_id
- api:editMessageText(msg.chat.id, id, nil, i18n("*Custom welcome message saved!*"), "Markdown")
+ api:editMessageText(msg.from.chat.id, id, nil, i18n("*Custom welcome message saved!*"), "Markdown")
end
end
end
+
if blocks[1] == 'new_chat_member' then
if not msg.service then return end
local extra
- if msg.from.id ~= msg.new_chat_member.id then extra = msg.from end
+ if msg.from.user.id ~= msg.new_chat_member.id then extra = msg.from.user end
u:logEvent(blocks[1], msg, extra)
local stop = ban_bots(self, msg)
if stop then return end
- apply_default_permissions(self, msg.chat.id, msg.new_chat_members)
+ apply_default_permissions(self, msg)
send_welcome(self, msg)
if db:get_user_setting(msg.new_chat_member.id, 'rules_on_join') then
- local rules = red:hget('chat:'..msg.chat.id..':info', 'rules')
+ local rules = red:hget('chat:'..msg.from.chat.id..':info', 'rules')
if rules ~= null then
api:sendMessage(msg.new_chat_member.id, rules, "Markdown")
end
diff --git a/lua/groupbutler/storage.lua b/lua/groupbutler/storage.lua
index 8e85ccc16..da99d144f 100644
--- a/lua/groupbutler/storage.lua
+++ b/lua/groupbutler/storage.lua
@@ -14,6 +14,11 @@ local PostgresStorage = {}
local MixedStorage = {}
+local function set_default(t, d)
+ local mt = {__index = function() return d end}
+ setmetatable(t, mt)
+end
+
local function enum(t)
local new_t = {}
for k,v in pairs(t) do
@@ -30,6 +35,15 @@ local chat_type = enum({
channel = 3,
})
+local chat_member_status = enum({
+ creator = 0,
+ administrator = 1,
+ member = 2,
+ restricted = 3,
+ left = 4,
+ kicked = 5,
+})
+
local function string_toboolean(v)
if v == false
or v == "false"
@@ -148,14 +162,69 @@ function RedisStorage:toggle_user_setting(user_id, setting)
self:set_user_setting(user_id, setting, not self:get_user_setting(user_id, setting))
end
-function RedisStorage:cache_user(user)
- if user.username then
- self.redis:hset("bot:usernames", "@"..user.username:lower(), user.id)
+function RedisStorage:cacheUser(user)
+ if rawget(user, "username") then
+ self.redis:hset("bot:usernames", "@"..rawget(user, "username"):lower(), user.id)
+ end
+end
+
+function RedisStorage:getUserId(username)
+ if username:byte(1) ~= string.byte("@") then
+ username = "@"..username
+ end
+ return tonumber(self.redis:hget("bot:usernames", username:lower()))
+end
+
+function RedisStorage:getUserProperty(user, property) -- luacheck: ignore
+end
+
+function RedisStorage:getChatProperty(chat, property)
+ if property == "title" then
+ local title = self.redis:get("chat:"..chat.id..":title")
+ if title == null then
+ return
+ end
+ return title
+ end
+end
+
+function RedisStorage:getChatAdministratorsCount(chat)
+ return self.redis:scard("cache:chat:"..chat.id..":admins")
+end
+
+function RedisStorage:getChatAdministratorsList(chat)
+ local admins = self.redis:smembers("cache:chat:"..chat.id..":admins") or {}
+ local owner = self.redis:get("cache:chat:"..chat.id..":owner")
+ if owner then
+ table.insert(admins, owner)
end
+ return admins
end
-function RedisStorage:get_user_id(username)
- return tonumber(self.redis:hget("bot:usernames", username))
+local is_admin_permission = {
+ can_be_edited = true,
+ can_change_info = true,
+ can_delete_messages = true,
+ can_invite_users = true,
+ can_restrict_members = true,
+ can_promote_members = true,
+ can_pin_messages = true,
+} set_default(is_admin_permission, false)
+
+function RedisStorage:getChatMemberProperty(member, property)
+ if is_admin_permission[property] then
+ local set = ("cache:chat:%s:%s:permissions"):format(member.chat.id, member.user.id)
+ return self.redis:sismember(set, property) == 1
+ end
+ if property == "status" then
+ if tonumber(self.redis:get("cache:chat:"..member.chat.id..":owner")) == member.user.id then
+ return "creator"
+ end
+ if self.redis:sismember("cache:chat:"..member.chat.id..":admins", member.user.id) == 1 then
+ return "administrator"
+ end
+ return nil
+ end
end
function RedisStorage:cacheChat(chat)
@@ -170,12 +239,36 @@ function RedisStorage:cacheChat(chat)
end
end
-function RedisStorage:getChatTitle(chat)
- local title = self.redis:get("chat:"..chat.id..":title")
- if title == null then
- return
+local admins_permissions = {
+ can_be_edited = false,
+ can_change_info = false,
+ can_delete_messages = false,
+ can_invite_users = false,
+ can_restrict_members = false,
+ can_promote_members = false,
+ can_pin_messages = false,
+}
+
+function RedisStorage:cacheAdmins(chat, list)
+ local set = 'cache:chat:'..chat.id..':admins'
+ local cache_time = config.bot_settings.cache_time.adminlist
+ self.redis:del(set)
+ for _, admin in pairs(list) do
+ if admin.status == 'creator' then
+ self.redis:set('cache:chat:'..chat.id..':owner', admin.user.id)
+ else
+ local set_permissions = "cache:chat:"..chat.id..":"..admin.user.id..":permissions"
+ self.redis:del(set_permissions)
+ for k, v in pairs(admin) do
+ if v and admins_permissions[k] then
+ self.redis:sadd(set_permissions, k)
+ end
+ end
+ self.redis:expire(set_permissions, cache_time)
+ end
+ self.redis:sadd(set, admin.user.id)
end
- return title
+ self.redis:expire(set, cache_time)
end
function RedisStorage:deleteChat(chat)
@@ -194,9 +287,7 @@ function RedisStorage:deleteChat(chat)
self.redis:del('chat:'..chat.id..':'..set)
end
- local owner_id = self.redis:get("cache:chat:"..chat.id..":owner")
local keys = {
- "cache:chat:"..chat.id..":"..owner_id..":permissions",
"cache:chat:"..chat.id..":admins",
"cache:chat:"..chat.id..":owner",
"chat:"..chat.id..":title",
@@ -205,6 +296,11 @@ function RedisStorage:deleteChat(chat)
"chat:"..chat.id..":pin",
"lang:"..chat.id,
}
+ local owner_id = self.redis:get("cache:chat:"..chat.id..":owner")
+ if owner_id
+ and owner_id ~= null then
+ table.insert(keys, "cache:chat:"..chat.id..":"..owner_id..":permissions")
+ end
for _,k in pairs(keys) do
self.redis:del(k)
end
@@ -219,6 +315,9 @@ function RedisStorage:deleteChat(chat)
end
end
+function RedisStorage:cacheChatMember(member) -- luacheck: ignore 212
+end
+
function RedisStorage:set_keepalive()
self.redis:set_keepalive()
end
@@ -228,17 +327,17 @@ function RedisStorage:get_reused_times()
end
local function is_user_property_optional(k)
- if k == "last_name"
+ if k == "is_bot"
+ or k == "last_name"
or k == "username"
or k == "language_code" then
return true
end
end
-function PostgresStorage:cache_user(user)
+function PostgresStorage:cacheUser(user)
local row = {
id = user.id,
- is_bot = user.is_bot,
first_name = self.pg:escape_literal(user.first_name)
}
for k, _ in pairs(user) do
@@ -247,11 +346,11 @@ function PostgresStorage:cache_user(user)
end
end
local username = ""
- if user.username then
+ if rawget(user, "username") then
username = 'UPDATE "user" SET username = NULL WHERE lower(username) = lower({username});\n'
end
- local insert = 'INSERT INTO "user" (id, is_bot, first_name'
- local values = ") VALUES ({id}, {is_bot}, {first_name}"
+ local insert = 'INSERT INTO "user" (id, first_name'
+ local values = ") VALUES ({id}, {first_name}"
local on_conflict = " ON CONFLICT (id) DO UPDATE SET first_name = {first_name}"
for k, _ in pairs(row) do
if is_user_property_optional(k) then
@@ -269,7 +368,7 @@ function PostgresStorage:cache_user(user)
return true
end
-function PostgresStorage:get_user_id(username)
+function PostgresStorage:getUserId(username)
if username:byte(1) == string.byte("@") then
username = username:sub(2)
end
@@ -282,6 +381,49 @@ function PostgresStorage:get_user_id(username)
return ok[1].id
end
+function PostgresStorage:getUserProperty(user, property)
+ local query = interpolate('SELECT {property} FROM "user" WHERE id = {id}', {
+ id = user.id,
+ property = property,
+ })
+ local ok = self.pg:query(query)
+ if not ok or not ok[1] or not ok[1][property] then
+ return nil
+ end
+ return ok[1][property]
+end
+
+function PostgresStorage:getChatProperty(chat, property)
+ local query = interpolate('SELECT {property} FROM "chat" WHERE id = {id}', {
+ id = chat.id,
+ property = property,
+ })
+ local ok = self.pg:query(query)
+ if not ok or not ok[1] or not ok[1][property] then
+ return nil
+ end
+ if property == "type" then
+ return chat_type[ok[1][property]]
+ end
+ return ok[1][property]
+end
+
+function PostgresStorage:getChatMemberProperty(member, property)
+ local query = interpolate('SELECT {property} FROM "chat_user" WHERE chat_id = {chat_id} AND user_id = {user_id}', {
+ chat_id = member.chat.id,
+ user_id = member.user.id,
+ property = property,
+ })
+ local ok = self.pg:query(query)
+ if not ok or not ok[1] or not ok[1][property] then
+ return nil
+ end
+ if property == "status" then
+ return chat_member_status[ok[1][property]]
+ end
+ return ok[1][property]
+end
+
local function is_chat_property_optional(k)
if k == "username"
or k == "invite_link" then
@@ -321,18 +463,155 @@ function PostgresStorage:cacheChat(chat)
return true
end
-function PostgresStorage:getChatTitle(chat)
- local query = interpolate('SELECT title FROM "chat" WHERE id = {id}', chat)
+function PostgresStorage:getChatAdministratorsCount(chat)
+ local row = {
+ chat_id = chat.id,
+ administrator = chat_member_status["administrator"],
+ }
+ local query = interpolate(
+ 'SELECT count(*) FROM "chat_user" WHERE chat_id = {chat_id} AND status = {administrator}', row)
local ok = self.pg:query(query)
- if not ok or not ok[1] or not ok[1].title then
- return false
+ if not ok or not ok[1] or not ok[1].count then
+ return 0
end
- return ok[1].title
+ return ok[1].count
+end
+
+function PostgresStorage:getChatAdministratorsList(chat)
+ local row = {
+ chat_id = chat.id,
+ creator = chat_member_status["creator"],
+ administrator = chat_member_status["administrator"],
+ }
+ local query = interpolate('SELECT user_id FROM "chat_user" WHERE chat_id = {chat_id}'..
+ 'AND (status = {creator} OR status = {administrator})', row)
+ local ok = self.pg:query(query)
+ if not ok or not ok[1] or not ok[1].user_id then
+ return nil
+ end
+ local retval = {}
+ for _,v in pairs(ok) do
+ table.insert(retval, v.user_id)
+ end
+ return retval
end
function PostgresStorage:deleteChat(chat)
local query = interpolate('DELETE FROM "chat" WHERE id = {id}', chat)
self.pg:query(query)
+ return true
+end
+
+local function isChatMemberPropertyOptional(status, k)
+ if status == "administrator" then
+ if k == "can_be_edited"
+ or k == "can_change_info"
+ or k == "can_delete_messages"
+ or k == "can_invite_users"
+ or k == "can_restrict_members"
+ or k == "can_promote_members"
+ or k == "can_pin_messages" then
+ -- or k == "can_post_messages" -- Channels only
+ -- or k == "can_edit_messages" then -- Channels only
+ return true
+ end
+ end
+ if status == "restricted" then
+ if k == "until_date"
+ or k == "can_send_messages"
+ or k == "can_send_media_messages"
+ or k == "can_send_other_messages"
+ or k == "can_add_web_page_previews" then
+ return true
+ end
+ end
+end
+
+function PostgresStorage:cacheChatMember(member)
+ if member.chat.type ~= "supergroup" then -- don't cache private chats, channels, etc.
+ return
+ end
+ do
+ local ok = self:cacheChat(member.chat)
+ if not ok then
+ return false
+ end
+ end
+ do
+ local ok = self:cacheUser(member.user)
+ if not ok then
+ return false
+ end
+ end
+ if not rawget(member, "status") then
+ log.warn("Tried to cache member without status {chat_id}, {user_id}", {
+ chat_id = member.chat.id,
+ user_id = member.user.id,
+ })
+ return false
+ end
+ local row = {
+ chat_id = member.chat.id,
+ user_id = member.user.id,
+ status = chat_member_status[member.status],
+ }
+ local insert = 'INSERT INTO "chat_user" (chat_id, user_id, status'
+ local values = ") VALUES ({chat_id}, {user_id}, {status}"
+ local on_conflict = ") ON CONFLICT (chat_id, user_id) DO UPDATE SET status = {status}"
+ for k, v in pairs(member) do
+ if isChatMemberPropertyOptional(member.status, k) then
+ row[k] = v
+ insert = insert..", "..k
+ values = values..", {"..k.."}"
+ on_conflict = on_conflict..", "..k.." = {"..k.."}"
+ end
+ end
+ local query = interpolate(insert..values..on_conflict, row)
+ local ok, err = self.pg:query(query)
+ if not ok then
+ log.err("Query {query} failed: {err}", {query=query, err=err})
+ end
+ return true
+end
+
+function PostgresStorage:wipeAdmins(chat)
+ local row = {
+ chat_id = chat.id,
+ member = chat_member_status["member"],
+ administrator = chat_member_status["administrator"],
+ }
+ local set = 'UPDATE "chat_user" SET status = {member}'
+ local where = ' WHERE chat_id = {chat_id} AND status = {administrator}'
+ for k, v in pairs(admins_permissions) do
+ row[k] = v
+ set = set..", "..k.." = {"..k.."}"
+ end
+ local query = interpolate(set..where, row)
+ local ok, err = self.pg:query(query)
+ if not ok then
+ log.err("Query {query} failed: {err}", {query=query, err=err})
+ return false
+ end
+ return true
+end
+
+function PostgresStorage:cacheAdmins(chat, list)
+ do
+ local ok = self:wipeAdmins(chat)
+ if not ok then
+ return false
+ end
+ end
+ for _, admin in pairs(list) do
+ admin.chat = chat
+ do
+ local ok = self:cacheChatMember(admin)
+ if not ok then
+ return false
+ end
+ end
+ end
+ return true
end
function PostgresStorage:set_keepalive()
@@ -343,21 +622,45 @@ function PostgresStorage:get_reused_times() -- luacheck: ignore 212
return "Unknown"
end
-function MixedStorage:cache_user(user)
- local res, ok = pcall(function() return self.postgres_storage:cache_user(user) end)
+function MixedStorage:cacheUser(user)
+ local res, ok = pcall(function() return self.postgres_storage:cacheUser(user) end)
if not res or not ok then
- self.redis_storage:cache_user(user)
+ self.redis_storage:cacheUser(user)
end
end
-function MixedStorage:get_user_id(username)
- local ok, id = pcall(function() return self.postgres_storage:get_user_id(username) end)
+function MixedStorage:getUserId(username)
+ local ok, id = pcall(function() return self.postgres_storage:getUserId(username) end)
if not ok or not id then
- return self.redis_storage:get_user_id(username)
+ return self.redis_storage:getUserId(username)
end
return id
end
+function MixedStorage:getUserProperty(user, property)
+ local ok, retval = pcall(function() return self.postgres_storage:getUserProperty(user, property) end)
+ if not ok or not retval then
+ return self.redis_storage:getUserProperty(user, property)
+ end
+ return retval
+end
+
+function MixedStorage:getChatProperty(chat, property)
+ local ok, retval = pcall(function() return self.postgres_storage:getChatProperty(chat, property) end)
+ if not ok or not retval then
+ return self.redis_storage:getChatProperty(chat, property)
+ end
+ return retval
+end
+
+function MixedStorage:getChatMemberProperty(member, property)
+ local ok, retval = pcall(function() return self.postgres_storage:getChatMemberProperty(member, property) end)
+ if not ok or not retval then
+ return self.redis_storage:getChatMemberProperty(member, property)
+ end
+ return retval
+end
+
function MixedStorage:cacheChat(chat)
local res, ok = pcall(function() return self.postgres_storage:cacheChat(chat) end)
if not res or not ok then
@@ -365,10 +668,18 @@ function MixedStorage:cacheChat(chat)
end
end
-function MixedStorage:getChatTitle(chat)
- local ok, title = pcall(function() return self.postgres_storage:getChatTitle(chat) end)
+function MixedStorage:getChatAdministratorsCount(chat)
+ local ok, title = pcall(function() return self.postgres_storage:getChatAdministratorsCount(chat) end)
if not ok or not title then
- return self.redis_storage:getChatTitle(chat)
+ return self.redis_storage:getChatAdministratorsCount(chat)
+ end
+ return title
+end
+
+function MixedStorage:getChatAdministratorsList(chat)
+ local ok, title = pcall(function() return self.postgres_storage:getChatAdministratorsList(chat) end)
+ if not ok or not title then
+ return self.redis_storage:getChatAdministratorsList(chat)
end
return title
end
@@ -378,6 +689,17 @@ function MixedStorage:deleteChat(chat)
self.redis_storage:deleteChat(chat)
end
+function MixedStorage:cacheChatMember(member)
+ pcall(function() return self.postgres_storage:cacheChatMember(member) end)
+end
+
+function MixedStorage:cacheAdmins(chat, list)
+ local res, ok = pcall(function() return self.postgres_storage:cacheAdmins(chat, list) end)
+ if not res or not ok then
+ self.redis_storage:cacheAdmins(chat, list)
+ end
+end
+
function MixedStorage:set_keepalive()
pcall(function() return self.postgres_storage:set_keepalive() end)
self.redis_storage:set_keepalive()
diff --git a/lua/groupbutler/user.lua b/lua/groupbutler/user.lua
index e9d43c968..142cd8483 100644
--- a/lua/groupbutler/user.lua
+++ b/lua/groupbutler/user.lua
@@ -1,3 +1,5 @@
+local log = require("groupbutler.logging")
+
local User = {}
local function p(self)
@@ -5,16 +7,94 @@ local function p(self)
end
function User:new(obj, private)
+ assert(obj.id or obj.username, "User: Missing obj.id or obj.username")
+ assert(private.api, "User: Missing private.api")
assert(private.db, "User: Missing private.db")
setmetatable(obj, {
- __index = self,
+ __index = function(s, index)
+ if self[index] then
+ return self[index]
+ end
+ return s:getProperty(index)
+ end,
__private = private,
+ __tostring = self.__tostring,
})
+ if not obj:checkId() then
+ return nil, "Username not found"
+ end
return obj
end
+function User:checkId()
+ local username = rawget(self, "username")
+ if username
+ and username:byte(1) == string.byte("@") then
+ self.username = username:sub(2)
+ end
+ local id = rawget(self, "id")
+ if not id then
+ id = p(self).db:getUserId(self.username)
+ self.id = id
+ if not id then
+ return false -- No cached id for this username
+ end
+ local user = p(self).api:getChat(id)
+ if not user -- Api call failed
+ or not user.username then -- User removed their username
+ return true -- Assuming it's the same user
+ end
+ if self.username ~= user.username then -- Got a different user than expected
+ User:new(user, p(self)):cache() -- Update cache with the different user so this doesn't happen again
+ return false
+ end
+ end
+ return true
+end
+
+function User:getProperty(index)
+ local property = rawget(self, index)
+ if property == nil then
+ property = p(self).db:getUserProperty(self, index)
+ if property == nil then
+ local ok = p(self).api:getChat(self.id)
+ if not ok then
+ log.warn("User: Failed to get {property} for {id}", {
+ property = index,
+ id = self.id,
+ })
+ return nil
+ end
+ for k,v in pairs(ok) do
+ self[k] = v
+ end
+ self:cache()
+ property = rawget(self, index)
+ end
+ self[index] = property
+ end
+ return property
+end
+
+function User:__tostring()
+ if self.first_name then
+ if self.last_name then
+ return self.first_name.." "..self.last_name
+ end
+ return self.first_name
+ end
+ if self.username then
+ return self.username
+ end
+ return self.id
+end
+
function User:cache()
- p(self).db:cache_user(self)
+ p(self).db:cacheUser(self)
+end
+
+function User:getLink()
+ return ('%s'):format(self.id, tostring(self):escape_html())
end
return User
diff --git a/lua/groupbutler/utilities.lua b/lua/groupbutler/utilities.lua
index afb886e08..63da5d3c6 100644
--- a/lua/groupbutler/utilities.lua
+++ b/lua/groupbutler/utilities.lua
@@ -2,6 +2,8 @@ local config = require "groupbutler.config"
local api_u = require "telegram-bot-api.utilities"
local log = require "groupbutler.logging"
local null = require "groupbutler.null"
+local User = require("groupbutler.user")
+local ChatMember = require("groupbutler.chatmember")
local http, HTTPS, ltn12, time_hires, sleep
if ngx then
http = require "resty.http"
@@ -41,41 +43,6 @@ local function set_default(t, d)
setmetatable(t, mt)
end
-function _M:banUser(chat_id, user_id, until_date)
- local api = p(self).api
- local api_err = p(self).api_err
- local ok, err = api:kickChatMember(chat_id, user_id, until_date) --try to kick. "code" is already specific
- if not ok then --if the user has been kicked, then...
- return nil, api_err:trans(err)
- end
- return ok --return res and not the text
-end
-
-function _M:kickUser(chat_id, user_id)
- local api = p(self).api
- local api_err = p(self).api_err
- local ok, err = api:kickChatMember(chat_id, user_id) --try to kick
- if not ok then --if the user has been kicked, then unban...
- return nil, api_err:trans(err)
- end
- api:unbanChatMember(chat_id, user_id)
- return ok
-end
-
-function _M:muteUser(chat_id, user_id)
- local api = p(self).api
- local api_err = p(self).api_err
- local ok, err = api:restrictChatMember{
- chat_id = chat_id,
- user_id = user_id,
- can_send_messages = false
- }
- if not ok then
- return nil, api_err:trans(err)
- end
- return ok
-end
-
-- Strings
-- Escape markdown for Telegram. This function makes non-clickable usernames,
@@ -127,12 +94,12 @@ end
-- otherwise it processes all available placeholders.
function _M:replaceholders(str, msg, ...)
if msg.new_chat_member then
- msg.from = msg.new_chat_member
+ msg.from.user = msg.new_chat_member
elseif msg.left_chat_member then
- msg.from = msg.left_chat_member
+ msg.from.user = msg.left_chat_member
end
- msg.chat.title = msg.chat.title and msg.chat.title or '-'
+ msg.from.chat.title = msg.from.chat.title and msg.from.chat.title or '-'
local tail_arguments = {...}
-- check that the second argument is a boolean and true
@@ -141,24 +108,24 @@ function _M:replaceholders(str, msg, ...)
local replace_map
if non_escapable then
replace_map = {
- name = msg.from.first_name,
- surname = msg.from.last_name and msg.from.last_name or '',
- username = msg.from.username and '@'..msg.from.username or '-',
- id = msg.from.id,
- title = msg.chat.title,
- rules = self:deeplink_constructor(msg.chat.id, "rules"),
+ name = msg.from.user.first_name,
+ surname = msg.from.user.last_name and msg.from.user.last_name or '',
+ username = msg.from.user.username and '@'..msg.from.user.username or '-',
+ id = msg.from.user.id,
+ title = msg.from.chat.title,
+ rules = self:deeplink_constructor(msg.from.chat.id, "rules"),
}
-- remove flag about escaping
table.remove(tail_arguments, 1)
else
replace_map = {
- name = msg.from.first_name:escape(),
- surname = msg.from.last_name and msg.from.last_name:escape() or '',
- username = msg.from.username and '@'..msg.from.username:escape() or '-',
- userorname = msg.from.username and '@'..msg.from.username:escape() or msg.from.first_name:escape(),
- id = msg.from.id,
- title = msg.chat.title:escape(),
- rules = self:deeplink_constructor(msg.chat.id, "rules"),
+ name = msg.from.user.first_name:escape(),
+ surname = msg.from.user.last_name and msg.from.user.last_name:escape() or '',
+ username = msg.from.user.username and '@'..msg.from.user.username:escape() or '-',
+ userorname = msg.from.user.username and '@'..msg.from.user.username:escape() or msg.from.user.first_name:escape(),
+ id = msg.from.user.id,
+ title = msg.from.chat.title:escape(),
+ rules = self:deeplink_constructor(msg.from.chat.id, "rules"),
}
end
@@ -170,23 +137,6 @@ function _M:replaceholders(str, msg, ...)
return str:gsub('$(%w+)', substitutions)
end
-function _M:is_allowed(_, chat_id, user_obj) -- action is not used anymore
- return self:is_admin(chat_id, user_obj.id)
-end
-
-function _M:can(chat_id, user_id, permission)
- local red = p(self).red
- if tonumber(red:get('cache:chat:'..chat_id..':owner')) == user_id then
- return true
- end
- local set = ("cache:chat:%s:%s:permissions"):format(chat_id, user_id)
- local set_admins = 'cache:chat:'..chat_id..':admins'
- if red:exists(set_admins) == 0 then
- self:cache_adminlist(chat_id)
- end
- return red:sismember(set, permission) == 1
-end
-
function _M:is_superadmin(user_id) -- luacheck: ignore 212
for i=1, #config.superadmins do
if tonumber(user_id) == config.superadmins[i] then
@@ -196,74 +146,14 @@ function _M:is_superadmin(user_id) -- luacheck: ignore 212
return false
end
--- Returns the admin status of the user. The first argument can be the message,
--- then the function checks the rights of the sender in the incoming chat.
-function _M:is_admin(chat_id, user_id)
- local red = p(self).red
- if type(chat_id) == 'table' then
- local msg = chat_id
- chat_id = msg.chat.id
- user_id = msg.from.id
- end
-
- local set = 'cache:chat:'..chat_id..':admins'
- if red:exists(set) == 0 then
- self:cache_adminlist(chat_id)
- end
- return red:sismember(set, user_id) ~= 0
-end
-
-function _M:is_owner(chat_id, user_id)
- local red = p(self).red
- if type(chat_id) == 'table' then
- local msg = chat_id
- chat_id = msg.chat.id
- user_id = msg.from.id
- end
-
- local hash = 'cache:chat:'..chat_id..':owner'
- local owner_id
- local res = true
- repeat
- owner_id = red:get(hash)
- if owner_id == null then
- res = self:cache_adminlist(chat_id)
- end
- until owner_id ~= null or not res
-
- if owner_id then
- if tonumber(owner_id) == tonumber(user_id) then
- return true
- end
- end
-
- return false
-end
-
-local adminspermissions = {
- can_change_info = true,
- can_delete_messages = true,
- can_invite_users = true,
- can_restrict_members = true,
- canpin_messages = true,
- canpromote_member = true
-}
-
-local function set_creatorpermissions(self, chat_id, user_id)
- local red = p(self).red
- local set = ("cache:chat:%s:%s:permissions"):format(chat_id, user_id)
- for k, _ in pairs(adminspermissions) do
- red:sadd(set, k)
- end
-end
-
-function _M:cache_adminlist(chat_id)
+function _M:cache_adminlist(chat)
local api = p(self).api
local red = p(self).red
+ local db = p(self).db
local global_lock = "bot:getadmin_lock"
- local chat_lock = "cache:chat:"..chat_id..":getadmin_lock"
- local set = 'cache:chat:'..chat_id..':admins'
+ local chat_lock = "cache:chat:"..chat.id..":getadmin_lock"
+ local set = 'cache:chat:'..chat.id..':admins'
if red:exists(global_lock) == 1
or red:exists(chat_lock) == 1 then
@@ -271,15 +161,12 @@ function _M:cache_adminlist(chat_id)
and (red:exists(global_lock) == 1 or red:exists(chat_lock) == 1) do
sleep(0.1)
end
- if red:exists(set) == 1 then
- return true, 0 -- Another concurrent request has just updated the adminlist
- end
end
- red:setex(chat_lock, 5, "")
- log.info('Saving the adminlist for: {chat_id}', {chat_id=chat_id})
+ red:setex(global_lock, 5, "")
+ log.info('Saving the adminlist for: {chat_id}', {chat_id=chat.id})
self:metric_incr("api_getchatadministrators_count")
- local ok, err = api:getChatAdministrators(chat_id)
+ local ok, err = api:getChatAdministrators(chat.id)
if not ok then
if err.retry_after then
red:setex(global_lock, err.retry_after, "")
@@ -289,45 +176,12 @@ function _M:cache_adminlist(chat_id)
self:metric_incr("api_getchatadministrators_error_count")
return false, err
end
- local cache_time = config.bot_settings.cache_time.adminlist
- local setpermissions
- red:del(set)
- for _, admin in pairs(ok) do
- if admin.status == 'creator' then
- red:set('cache:chat:'..chat_id..':owner', admin.user.id)
- set_creatorpermissions(self, chat_id, admin.user.id)
- else
- setpermissions = "cache:chat:"..chat_id..":"..admin.user.id..":permissions"
- red:del(setpermissions)
- for k, v in pairs(admin) do
- if v and adminspermissions[k] then red:sadd(setpermissions, k) end
- end
- red:expire(setpermissions, cache_time)
- end
-
- red:sadd(set, admin.user.id)
- self:demote(chat_id, admin.user.id)
- end
- red:expire(set, cache_time)
+ db:cacheAdmins(chat, ok)
return true, #ok or 0
end
-function _M:get_cached_admins_list(chat_id, second_try)
- local red = p(self).red
- local hash = 'cache:chat:'..chat_id..':admins'
- local list = red:smembers(hash)
- if not list or not next(list) then
- self:cache_adminlist(chat_id)
- if not second_try then
- return self:get_cached_admins_list(chat_id, true)
- end
- return false
- end
- return list
-end
-
function _M:is_blocked_global(id)
local red = p(self).red
return red:sismember('bot:blocked', id) ~= 0
@@ -397,35 +251,6 @@ function _M:get_date(timestamp) -- luacheck: ignore 212
return os.date('%d/%m/%y', timestamp)
end
--- Resolves username. Returns ID of user if it was early stored in date base.
--- Argument username must begin with symbol @ (commercial 'at')
-function _M:resolve_user(username)
- local api = p(self).api
- assert(username:byte(1) == string.byte('@'))
- username = username:lower()
-
- local stored_id = p(self).db:get_user_id(username)
- if not stored_id then return false end
-
- local user_obj = api:getChat(stored_id)
- if not user_obj then
- return stored_id
- end
- if not user_obj.username then
- return stored_id
- end
-
- -- Users could change their username
- if username ~= '@' .. user_obj.username:lower() then
- p(self).db:cache_user(user_obj)
- -- And return false because this user not the same that asked
- return false
- end
-
- assert(stored_id == user_obj.id)
- return user_obj.id
-end
-
function _M:reply_markup_from_text(text) -- luacheck: ignore 212
local clean_text = text
local n = 0
@@ -452,53 +277,6 @@ function _M:demote(chat_id, user_id)
return removed == 1
end
-function _M:migrate_chat_info(old, new, on_request)
- local api = p(self).api
- local red = p(self).red
- if not old or not new then
- return false
- end
-
- for hash_name, _ in pairs(config.chat_settings) do
- local old_t = red:hgetall('chat:'..old..':'..hash_name)
- if next(old_t) then
- for key, val in pairs(old_t) do
- red:hset('chat:'..new..':'..hash_name, key, val)
- end
- end
- end
-
- for _, hash_name in pairs(config.chat_hashes) do
- local old_t = red:hgetall('chat:'..old..':'..hash_name)
- if next(old_t) then
- for key, val in pairs(old_t) do
- red:hset('chat:'..new..':'..hash_name, key, val)
- end
- end
- end
-
- for i=1, #config.chat_sets do
- local old_t = red:smembers('chat:'..old..':'..config.chat_sets[i])
- if next(old_t) then
- red:sadd('chat:'..new..':'..config.chat_sets[i], unpack(old_t))
- end
- end
-
- if on_request then
- api:send_message(new, "Should be done")
- end
-end
-
--- Return user mention for output a text
-function _M:getname_final(user)
- return self:getname_link(user) or ''..user.first_name:escape_html()..'
'
-end
-
--- Return link to user profile or false, if they don't have login
-function _M:getname_link(user) -- luacheck: ignore 212
- return ('%s'):format('tg://user?id='..user.id, user.first_name:escape_html())
-end
-
function _M:bash(str) -- luacheck: ignore 212
local cmd = io.popen(str)
local result = cmd:read('*all')
@@ -512,8 +290,7 @@ function _M:telegram_file_link(res) -- luacheck: ignore 212
end
function _M:is_silentmode_on(chat_id)
- local red = p(self).red
- return red:hget("chat:"..chat_id..":settings", "Silent") == "on"
+ return p(self).db:get_chat_setting(chat_id, "Silent")
end
function _M:getRules(chat_id)
@@ -527,42 +304,39 @@ function _M:getRules(chat_id)
return rules
end
-function _M:getAdminlist(chat_id)
- local api = p(self).api
+function _M:getAdminlist(chat)
local i18n = p(self).i18n
- local list, code = self:get_cached_admins_list(chat_id)
+ local db = p(self).db
+ local list = db:getChatAdministratorsList(chat)
if not list then
- return false, code
+ return false
end
- local creator = ''
- local adminlist = ''
+ local creator = ""
+ local adminlist = ""
local count = 1
for _, user_id in pairs(list) do
- local s = ' ├ '
- -- TODO: Cache admin names nad usernames
- local admin = api:getChatMember(chat_id, user_id)
- if admin and admin.status == 'administrator' then
- local name = admin.user.first_name
- if admin.user.username then
- name = ('%s'):format(admin.user.username, name:escape_html())
- else
- name = name:escape_html()
+ local s = " ├ "
+ local admin = ChatMember:new({
+ chat = chat,
+ user = User:new({id=user_id}, p(self)),
+ }, p(self))
+ if admin.status == "administrator" then
+ if count + 1 == #list then
+ s = " └ "
end
- if count + 1 == #list then s = ' └ ' end
- adminlist = adminlist..s..name..'\n'
+ adminlist = adminlist..s..admin.user:getLink().."\n"
count = count + 1
- elseif admin and admin.status == 'creator' then
- creator = admin.user.first_name
- if admin.user.username then
- creator = ('%s'):format(admin.user.username, creator:escape_html())
- else
- creator = creator:escape_html()
- end
+ end
+ if admin.status == "creator" then
+ creator = admin.user:getLink()
end
end
- if adminlist == '' then adminlist = '-' end
- if creator == '' then creator = '-' end
-
+ if adminlist == "" then
+ adminlist = "-"
+ end
+ if creator == "" then
+ creator = "-"
+ end
return i18n("👤 Creator\n└ %s\n\n👥 Admins (%d)\n%s"):format(creator, #list - 1, adminlist)
end
@@ -716,27 +490,26 @@ function _M:sendStartMe(msg)
local keyboard = {
inline_keyboard = {{{text = i18n("Start me"), url = 'https://telegram.me/'..bot.username}}}
}
- api:sendMessage(msg.chat.id, i18n("please message me first so I can message you_"), 'Markdown', nil, nil, nil,
+ api:sendMessage(msg.from.chat.id, i18n("_Please message me first so I can message you_"), 'Markdown', nil, nil, nil,
keyboard)
end
-function _M:initGroup(chat_id)
+function _M:initGroup(chat)
local red = p(self).red
- local db = p(self).db
for set, setting in pairs(config.chat_settings) do
- local hash = 'chat:'..chat_id..':'..set
+ local hash = 'chat:'..chat.id..':'..set
for field, value in pairs(setting) do
red:hset(hash, field, value)
end
end
- self:cache_adminlist(chat_id) --init admin cache
+ self:cache_adminlist(chat) --init admin cache
--save group id
- red:sadd('bot:groupsid', chat_id)
+ red:sadd('bot:groupsid', chat.id)
--remove the group id from the list of dead groups
- red:srem('bot:groupsid:removed', chat_id)
- db:cacheChat({id=chat_id, type="supergroup"})
+ red:srem('bot:groupsid:removed', chat.id)
+ chat:cache()
end
local function empty_modlist(self, chat_id)
@@ -758,89 +531,23 @@ function _M:remGroup(chat_id)
db:deleteChat({id=chat_id})
end
-function _M:getnames_complete(msg)
- local api = p(self).api
- local i18n = p(self).i18n
- local admin, kicked
-
- admin = self:getname_link(msg.from)
-
- if msg.reply then
- kicked = self:getname_link(msg.reply.from)
- elseif msg.text:match(config.cmd..'%w%w%w%w?%w?%s(@[%w_]+)%s?') then
- local username = msg.text:match('%s(@[%w_]+)')
- kicked = username
- elseif msg.mention_id then
- for _, entity in pairs(msg.entities) do
- if entity.user then
- kicked = self:getname_link(entity.user)
- end
- end
- elseif msg.text:match(config.cmd..'%w%w%w%w?%w?%s(%d+)') then
- local id = msg.text:match(config.cmd..'%w%w%w%w?%w?%s(%d+)')
- local res = api:getChatMember(msg.chat.id, id)
- if res then
- kicked = self:getname_final(res.user)
- end
- end
-
- -- TODO: Actually fix this
- if not kicked then kicked = i18n("Someone") end
- if not admin then admin = i18n("Someone") end
- return admin, kicked
-end
-
-function _M:get_user_id(msg, blocks)
- local i18n = p(self).i18n
- --if no user id: returns false and the msg id of the translation for the problem
- if not msg.reply and not blocks[2] then
- return false, i18n("Reply to a user or mention them")
- end
-
- if msg.reply then
- if msg.reply.new_chat_member then
- msg.reply.from = msg.reply.new_chat_member
- end
- return msg.reply.from.id
- end
-
- if blocks[2]:byte(1) == string.byte("@") then
- local id = self:resolve_user(blocks[2])
- if id then
- return id
- end
- end
-
- if msg.mention_id then
- return msg.mention_id
- end
-
- local id = blocks[2]:match("%d+")
- if id then
- return id
- end
-
- return false, i18n([[I've never seen this user before.
-This command works by reply, username, user ID or text mention.
-If you're using it by username and want to teach me who the user is, forward me one of their messages]])
-end
-
function _M:logEvent(event, msg, extra)
local api = p(self).api
local bot = p(self).bot
local red = p(self).red
local i18n = p(self).i18n
- local log_id = red:hget('bot:chatlogs', msg.chat.id)
+ local log_id = red:hget('bot:chatlogs', msg.from.chat.id)
-- self:dump(extra)
if not log_id or log_id == null then return end
- local is_loggable = red:hget('chat:'..msg.chat.id..':tolog', event)
+ local is_loggable = red:hget('chat:'..msg.from.chat.id..':tolog', event)
if not is_loggable == 'yes' then return end
local text, reply_markup
- local chat_info = i18n("Chat: %s [#chat%d]"):format(msg.chat.title:escape_html(), msg.chat.id * -1)
- local member = ("%s [@%s] [#id%d]"):format(msg.from.first_name:escape_html(), msg.from.username or '-', msg.from.id)
+ local chat_info = i18n("Chat: %s [#chat%d]"):format(msg.from.chat.title:escape_html(), msg.from.chat.id * -1)
+ local member = ("%s [@%s] [#id%d]"):format(msg.from.user.first_name:escape_html(), msg.from.user.username or '-',
+ msg.from.user.id)
local log_event = {
mediawarn = function()
@@ -895,8 +602,8 @@ function _M:logEvent(event, msg, extra)
local member2 = ("%s [@%s] [#id%d]"):format(msg.new_chat_member.first_name:escape_html(),
msg.new_chat_member.username or '-', msg.new_chat_member.id)
text = i18n('%s\n• %s\n• User: %s'):format('#NEW_MEMBER', chat_info, member2)
- if extra then --extra == msg.from
- text = text..i18n("\n• Added by: %s [#id%d]"):format(self:getname_final(extra), extra.id)
+ if extra then --extra == msg.from.user
+ text = text..i18n("\n• Added by: %s [#id%d]"):format(extra:getLink(), extra.id)
end
end,
-- events that requires user + admin
@@ -910,7 +617,9 @@ function _M:logEvent(event, msg, extra)
--motivation: motivation
text = i18n(
'#%s\n• Admin: %s [#id%d]\n• %s\n• User: %s [#id%d]\n• Count: %d/%d
'
- ):format(event:upper(), extra.admin, msg.from.id, chat_info, extra.user, extra.user_id, extra.warns, extra.warnmax)
+ ):format(event:upper(), tostring(extra.admin), msg.from.user.id, chat_info, tostring(extra.user), extra.user_id,
+ extra.warns, extra.warnmax
+ )
end,
nowarn = function()
--WARNS REMOVED
@@ -918,11 +627,11 @@ function _M:logEvent(event, msg, extra)
--user name formatted: user
--user id: user_id
text = i18n("#%s\n• Admin: %s [#id%s]\n• %s\n• User: %s [#id%s]"):format(
- 'WARNS_RESET', extra.admin, msg.from.id, chat_info, extra.user, tostring(extra.user_id))
+ 'WARNS_RESET', extra.admin, msg.from.user.id, chat_info, extra.user, extra.user_id)
end,
block = function() -- or unblock
text = i18n('#%s\n• Admin: %s [#id%s]\n• %s\n'
- ):format(event:upper(), self:getname_final(msg.from), msg.from.id, chat_info)
+ ):format(event:upper(), msg.from.user, msg.from.user.id, chat_info)
if extra.n then
text = text..i18n('• Users involved: %d'):format(extra.n)
elseif extra.user then
@@ -939,7 +648,8 @@ function _M:logEvent(event, msg, extra)
--motivation: motivation
text = i18n(
'#%s\n• Admin: %s [#id%s]\n• %s\n• User: %s [#id%s]\n• Duration: %d days, %d hours'
- ):format(event:upper(), extra.admin, msg.from.id, chat_info, extra.user, tostring(extra.user_id), extra.d, extra.h)
+ ):format(event:upper(), tostring(extra.admin), msg.from.user.id, chat_info, tostring(extra.user),
+ extra.user_id, extra.d, extra.h)
end,
ban = function() -- or kick or unban
--BAN OR KICK OR UNBAN
@@ -948,7 +658,7 @@ function _M:logEvent(event, msg, extra)
--user id: user_id
--motivation: motivation
text = i18n('#%s\n• Admin: %s [#id%s]\n• %s\n• User: %s [#id%s]'):format(
- event:upper(), extra.admin, msg.from.id, chat_info, extra.user, tostring(extra.user_id))
+ event:upper(), tostring(extra.admin), msg.from.user.id, chat_info, tostring(extra.user), extra.user_id)
end,
} set_default(log_event, function()
text = i18n('#%s\n• %s\n• By: %s'):format(event:upper(), chat_info, member)
@@ -965,23 +675,24 @@ function _M:logEvent(event, msg, extra)
reply_markup = {
inline_keyboard = {{{
text = i18n("Unban"),
- callback_data = ("logcb:un%s:%d:%d"):format(event, extra.user_id, msg.chat.id)
+ callback_data = ("logcb:un%s:%d:%d"):format(event, extra.user_id, msg.from.chat.id)
}}}
}
end
if extra then
- if extra.hammered then
+ if rawget(extra, "hammered") then
text = text..i18n("\n• Action: #%s"):format(extra.hammered:upper())
end
- if extra.motivation then
+ if rawget(extra, "motivation") then
text = text..i18n('\n• Reason: %s'):format(extra.motivation:escape_html())
end
end
- if msg.chat.username then
- text = text..
- ('\n• %s'):format(msg.chat.username, msg.message_id, i18n('Go to the message'))
+ if msg.from.chat.username then
+ text = text..('\n• %s'):format(
+ msg.from.chat.username, msg.message_id, i18n('Go to the message')
+ )
end
local ok, err = api:send_message{
@@ -992,7 +703,7 @@ function _M:logEvent(event, msg, extra)
reply_markup = reply_markup
}
if not ok and err.description:match("chat not found") then
- red:hdel('bot:chatlogs', msg.chat.id)
+ red:hdel('bot:chatlogs', msg.from.chat.id)
end
end
diff --git a/schema/2.sql b/schema/2.sql
index 00633aca7..595802422 100644
--- a/schema/2.sql
+++ b/schema/2.sql
@@ -21,53 +21,6 @@ CREATE TABLE "chat" (
CONSTRAINT chat_pkey PRIMARY KEY (id)
);
--- CREATE TABLE "chat_user" (
--- chat_id bigint NOT NULL REFERENCES "chat"(id),
--- user_id integer NOT NULL REFERENCES "user"(id),
--- status smallint NOT NULL,
-
--- created_at timestamptz DEFAULT now() NOT NULL,
--- updated_at timestamptz DEFAULT now() NOT NULL,
-
--- CONSTRAINT chat_user_pkey PRIMARY KEY (chat_id, user_id)
--- );
-
--- CREATE TABLE "chat_user_admin" (
--- chat_id bigint NOT NULL REFERENCES "chat"(id),
--- user_id integer NOT NULL REFERENCES "user"(id),
-
--- can_be_edited boolean DEFAULT false NOT NULL,
--- can_change_info boolean DEFAULT false NOT NULL,
--- can_delete_messages boolean DEFAULT false NOT NULL,
--- can_invite_users boolean DEFAULT false NOT NULL,
--- can_restrict_members boolean DEFAULT false NOT NULL,
--- can_pin_messages boolean DEFAULT false NOT NULL,
--- can_promote_members boolean DEFAULT false NOT NULL,
--- can_post_messages boolean DEFAULT false NOT NULL, -- Channels only
--- can_edit_messages boolean DEFAULT false NOT NULL, -- Channels only
-
--- created_at timestamptz DEFAULT now() NOT NULL,
--- updated_at timestamptz DEFAULT now() NOT NULL,
-
--- CONSTRAINT chat_user_admin_pkey PRIMARY KEY (chat_id, user_id)
--- );
-
--- CREATE TABLE "chat_user_restricted" (
--- chat_id bigint NOT NULL REFERENCES "chat"(id),
--- user_id integer NOT NULL REFERENCES "user"(id),
-
--- until_date timestamptz, -- Date when restrictions will be lifted for this user, if ever
--- can_send_messages boolean DEFAULT true NOT NULL,
--- can_send_media_messages boolean DEFAULT true NOT NULL, -- implies can_send_messages
--- can_send_other_messages boolean DEFAULT true NOT NULL, -- implies can_send_media_messages
--- can_add_web_page_previews boolean DEFAULT true NOT NULL -- implies can_send_media_messages
-
--- created_at timestamptz DEFAULT now() NOT NULL,
--- updated_at timestamptz DEFAULT now() NOT NULL,
-
--- CONSTRAINT chat_user_restricted_pkey PRIMARY KEY (chat_id, user_id)
--- );
-
--
-- Triggers
--
@@ -76,20 +29,3 @@ CREATE TRIGGER set_updated_at
BEFORE UPDATE ON "chat"
FOR EACH ROW
EXECUTE PROCEDURE trigger_set_updated_at();
-
--- CREATE TRIGGER set_updated_at
--- BEFORE UPDATE ON "chat_user"
--- FOR EACH ROW
--- EXECUTE PROCEDURE trigger_set_updated_at();
-
---
--- Foreign Keys
---
-
--- ALTER TABLE chat_user
--- ADD CONSTRAINT chat_user_chat_id_fkey FOREIGN KEY (chat_id)
--- REFERENCES "chat"(id) ON UPDATE CASCADE ON DELETE CASCADE;
-
--- ALTER TABLE chat_user
--- ADD CONSTRAINT chat_user_user_id_fkey FOREIGN KEY (user_id)
--- REFERENCES "user"(id) ON UPDATE CASCADE ON DELETE CASCADE;
diff --git a/schema/3.sql b/schema/3.sql
new file mode 100644
index 000000000..3af2c55ce
--- /dev/null
+++ b/schema/3.sql
@@ -0,0 +1,47 @@
+--
+-- Tables
+--
+
+CREATE TABLE "chat_user" (
+ chat_id bigint NOT NULL REFERENCES "chat"(id) ON DELETE CASCADE,
+ user_id integer NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
+ status smallint NOT NULL,
+
+ -- status == administrator only
+ can_be_edited boolean DEFAULT false NOT NULL,
+ can_change_info boolean DEFAULT false NOT NULL,
+ can_delete_messages boolean DEFAULT false NOT NULL,
+ can_invite_users boolean DEFAULT false NOT NULL,
+ can_restrict_members boolean DEFAULT false NOT NULL,
+ can_pin_messages boolean DEFAULT false NOT NULL,
+ can_promote_members boolean DEFAULT false NOT NULL,
+ -- can_post_messages boolean DEFAULT false NOT NULL, -- Channels only
+ -- can_edit_messages boolean DEFAULT false NOT NULL, -- Channels only
+
+ -- status == restricted only
+ until_date timestamptz,
+ can_send_messages boolean DEFAULT true NOT NULL,
+ can_send_media_messages boolean DEFAULT true NOT NULL, -- implies can_send_messages
+ can_send_other_messages boolean DEFAULT true NOT NULL, -- implies can_send_media_messages
+ can_add_web_page_previews boolean DEFAULT true NOT NULL, -- implies can_send_media_messages
+
+ created_at timestamptz DEFAULT now() NOT NULL,
+ updated_at timestamptz DEFAULT now() NOT NULL,
+
+ CONSTRAINT chat_user_pkey PRIMARY KEY (chat_id, user_id)
+);
+
+--
+-- Triggers
+--
+
+CREATE TRIGGER set_updated_at
+ BEFORE UPDATE ON "chat_user"
+ FOR EACH ROW
+ EXECUTE PROCEDURE trigger_set_updated_at();
+
+--
+-- Updates
+--
+
+ALTER TABLE "user" ALTER COLUMN is_bot DROP NOT NULL;