commit
d1166c06a0
@ -1 +1 @@
|
||||
Subproject commit 80384323790471d254c7db5c237a49dc62624378
|
||||
Subproject commit d2229228b92117899d65cd549a1f2853057b255b
|
18
README.md
18
README.md
@ -1,14 +1,12 @@
|
||||
![img](https://ci.appveyor.com/api/projects/status/gmu6b3ltc80hr3k9?svg=true)
|
||||
[![Discord](https://discordapp.com/api/guilds/117523346618318850/widget.png)](https://discord.gg/0ehQwTK2RBjAxzEY)
|
||||
[![Discord](https://discordapp.com/api/guilds/117523346618318850/widget.png)](https://discord.gg/nadekobot)
|
||||
[![Documentation Status](https://readthedocs.org/projects/nadekobot/badge/?version=latest)](http://nadekobot.readthedocs.io/en/latest/?badge=latest)
|
||||
# NadekoBot
|
||||
[![nadeko1](https://cdn.discordapp.com/attachments/155726317222887425/252095170676391936/A1.jpg)](https://discordapp.com/oauth2/authorize?client_id=170254782546575360&scope=bot&permissions=66186303)
|
||||
[![nadeko2](https://cdn.discordapp.com/attachments/155726317222887425/252095207514832896/A2.jpg)](http://nadekobot.readthedocs.io/en/latest/Commands%20List/)
|
||||
|
||||
##For Update, Help and Guidlines
|
||||
|
||||
`Follow me on twitter for updates. | Join my Discord server if you need help. | Read the Docs for hosting guides.`
|
||||
|
||||
[![twitter](https://cdn.discordapp.com/attachments/155726317222887425/252192520094613504/twiter_banner.JPG)](https://twitter.com/TheNadekoBot) [![discord](https://cdn.discordapp.com/attachments/155726317222887425/252192415673221122/discord_banner.JPG)](https://discord.gg/0ehQwTK2RBjAxzEY) [![Wiki](https://cdn.discordapp.com/attachments/155726317222887425/252192472849973250/read_the_docs_banner.JPG)](http://nadekobot.readthedocs.io/en/latest/)
|
||||
[![nadeko0](https://cdn.discordapp.com/attachments/266240393639755778/281920716809699328/part1.png)](http://nadekobot.xyz)
|
||||
[![nadeko1](https://cdn.discordapp.com/attachments/266240393639755778/281920134967328768/part2.png)](https://discordapp.com/oauth2/authorize?client_id=170254782546575360&scope=bot&permissions=66186303)
|
||||
[![nadeko2](https://cdn.discordapp.com/attachments/266240393639755778/281920161311883264/part3.png)](http://nadekobot.readthedocs.io/en/latest/Commands%20List/)
|
||||
|
||||
##For Update, Help and Guidelines
|
||||
|
||||
| [![twitter](https://cdn.discordapp.com/attachments/155726317222887425/252192520094613504/twiter_banner.JPG)](https://twitter.com/TheNadekoBot) | [![discord](https://cdn.discordapp.com/attachments/266240393639755778/281920766490968064/discord.png)](https://discord.gg/nadekobot) | [![Wiki](https://cdn.discordapp.com/attachments/266240393639755778/281920793330581506/datcord.png)](http://nadekobot.readthedocs.io/en/latest/)
|
||||
| --- | --- | --- |
|
||||
| Follow me on Twitter for updates. | Join my Discord server if you need help. | Read the Docs for hosting guides. |
|
@ -18,56 +18,6 @@ You can support the project on patreon: <https://patreon.com/nadekobot> or paypa
|
||||
### Administration
|
||||
Command and aliases | Description | Usage
|
||||
----------------|--------------|-------
|
||||
`.voice+text` `.v+t` | Creates a text channel for each voice channel only users in that voice channel can see.If you are server owner, keep in mind you will see them all the time regardless. **Requires ManageRoles server permission.** **Requires ManageChannels server permission.** | `.voice+text`
|
||||
`.cleanvplust` `.cv+t` | Deletes all text channels ending in `-voice` for which voicechannels are not found. Use at your own risk. **Requires ManageChannels server permission.** **Requires ManageRoles server permission.** | `.cleanv+t`
|
||||
`.greetdel` `.grdel` | Sets the time it takes (in seconds) for greet messages to be auto-deleted. Set 0 to disable automatic deletion. **Requires ManageServer server permission.** | `.greetdel 0` or `.greetdel 30`
|
||||
`.greet` | Toggles anouncements on the current channel when someone joins the server. **Requires ManageServer server permission.** | `.greet`
|
||||
`.greetmsg` | Sets a new join announcement message which will be shown in the server's channel. Type %user% if you want to mention the new member. Using it with no message will show the current greet message. **Requires ManageServer server permission.** | `.greetmsg Welcome, %user%.`
|
||||
`.greetdm` | Toggles whether the greet messages will be sent in a DM (This is separate from greet - you can have both, any or neither enabled). **Requires ManageServer server permission.** | `.greetdm`
|
||||
`.greetdmmsg` | Sets a new join announcement message which will be sent to the user who joined. Type %user% if you want to mention the new member. Using it with no message will show the current DM greet message. **Requires ManageServer server permission.** | `.greetdmmsg Welcome to the server, %user%`.
|
||||
`.bye` | Toggles anouncements on the current channel when someone leaves the server. **Requires ManageServer server permission.** | `.bye`
|
||||
`.byemsg` | Sets a new leave announcement message. Type %user% if you want to show the name the user who left. Type %id% to show id. Using this command with no message will show the current bye message. **Requires ManageServer server permission.** | `.byemsg %user% has left.`
|
||||
`.byedel` | Sets the time it takes (in seconds) for bye messages to be auto-deleted. Set 0 to disable automatic deletion. **Requires ManageServer server permission.** | `.byedel 0` or `.byedel 30`
|
||||
`.leave` | Makes Nadeko leave the server. Either name or id required. **Bot Owner only.** | `.leave 123123123331`
|
||||
`.die` | Shuts the bot down. **Bot Owner only.** | `.die`
|
||||
`.setname` `.newnm` | Gives the bot a new name. **Bot Owner only.** | `.newnm BotName`
|
||||
`.setstatus` | Sets the bot's status. (Online/Idle/Dnd/Invisible) **Bot Owner only.** | `.setstatus Idle`
|
||||
`.setavatar` `.setav` | Sets a new avatar image for the NadekoBot. Argument is a direct link to an image. **Bot Owner only.** | `.setav http://i.imgur.com/xTG3a1I.jpg`
|
||||
`.setgame` | Sets the bots game. **Bot Owner only.** | `.setgame with snakes`
|
||||
`.setstream` | Sets the bots stream. First argument is the twitch link, second argument is stream name. **Bot Owner only.** | `.setstream TWITCHLINK Hello`
|
||||
`.send` | Sends a message to someone on a different server through the bot. Separate server and channel/user ids with `|` and prepend channel id with `c:` and user id with `u:`. **Bot Owner only.** | `.send serverid|c:channelid message` or `.send serverid|u:userid message`
|
||||
`.announce` | Sends a message to all servers' general channel bot is connected to. **Bot Owner only.** | `.announce Useless spam`
|
||||
`.adsarm` | Toggles the automatic deletion of confirmations for .iam and .iamn commands. **Requires ManageMessages server permission.** | `.adsarm`
|
||||
`.asar` | Adds a role to the list of self-assignable roles. **Requires ManageRoles server permission.** | `.asar Gamer`
|
||||
`.rsar` | Removes a specified role from the list of self-assignable roles. **Requires ManageRoles server permission.** | `.rsar`
|
||||
`.lsar` | Lists all self-assignable roles. | `.lsar`
|
||||
`.togglexclsar` `.tesar` | Toggles whether the self-assigned roles are exclusive. (So that any person can have only one of the self assignable roles) **Requires ManageRoles server permission.** | `.tesar`
|
||||
`.iam` | Adds a role to you that you choose. Role must be on a list of self-assignable roles. | `.iam Gamer`
|
||||
`.iamnot` `.iamn` | Removes a role to you that you choose. Role must be on a list of self-assignable roles. | `.iamn Gamer`
|
||||
`.slowmode` | Toggles slowmode. Disable by specifying no parameters. To enable, specify a number of messages each user can send, and an interval in seconds. For example 1 message every 5 seconds. **Requires ManageMessages server permission.** | `.slowmode 1 5` or `.slowmode`
|
||||
`.antiraid` | Sets an anti-raid protection on the server. First argument is number of people which will trigger the protection. Second one is a time interval in which that number of people needs to join in order to trigger the protection, and third argument is punishment for those people (Kick, Ban, Mute) **Requires Administrator server permission.** | `.antiraid 5 20 Kick`
|
||||
`.antispam` | Stops people from repeating same message X times in a row. You can specify to either mute, kick or ban the offenders. **Requires Administrator server permission.** | `.antispam 3 Mute` or `.antispam 4 Kick` or `.antispam 6 Ban`
|
||||
`.antispamignore` | Toggles whether antispam ignores current channel. Antispam must be enabled. | `.antispamignore`
|
||||
`.antilist` `.antilst` | Shows currently enabled protection features. | `.antilist`
|
||||
`.rotateplaying` `.ropl` | Toggles rotation of playing status of the dynamic strings you previously specified. **Bot Owner only.** | `.ropl`
|
||||
`.addplaying` `.adpl` | Adds a specified string to the list of playing strings to rotate. Supported placeholders: %servers%, %users%, %playing%, %queued% **Bot Owner only.** | `.adpl`
|
||||
`.listplaying` `.lipl` | Lists all playing statuses with their corresponding number. **Bot Owner only.** | `.lipl`
|
||||
`.removeplaying` `.rmpl` `.repl` | Removes a playing string on a given number. **Bot Owner only.** | `.rmpl`
|
||||
`.setmuterole` | Sets a name of the role which will be assigned to people who should be muted. Default is nadeko-mute. **Requires ManageRoles server permission.** | `.setmuterole Silenced`
|
||||
`.mute` | Mutes a mentioned user both from speaking and chatting. **Requires ManageRoles server permission.** **Requires MuteMembers server permission.** | `.mute @Someone`
|
||||
`.unmute` | Unmutes a mentioned user previously muted with `.mute` command. **Requires ManageRoles server permission.** **Requires MuteMembers server permission.** | `.unmute @Someone`
|
||||
`.chatmute` | Prevents a mentioned user from chatting in text channels. **Requires ManageRoles server permission.** | `.chatmute @Someone`
|
||||
`.chatunmute` | Removes a mute role previously set on a mentioned user with `.chatmute` which prevented him from chatting in text channels. **Requires ManageRoles server permission.** | `.chatunmute @Someone`
|
||||
`.voicemute` | Prevents a mentioned user from speaking in voice channels. **Requires MuteMembers server permission.** | `.voicemute @Someone`
|
||||
`.voiceunmute` | Gives a previously voice-muted user a permission to speak. **Requires MuteMembers server permission.** | `.voiceunmute @Someguy`
|
||||
`.migratedata` | Migrate data from old bot configuration **Bot Owner only.** | `.migratedata`
|
||||
`.logserver` | Enables or Disables ALL log events. If enabled, all log events will log to this channel. **Requires Administrator server permission.** **Bot Owner only.** | `.logserver enable` or `.logserver disable`
|
||||
`.logignore` | Toggles whether the .logserver command ignores this channel. Useful if you have hidden admin channel and public log channel. **Requires Administrator server permission.** **Bot Owner only.** | `.logignore`
|
||||
`.logevents` | Shows a list of all events you can subscribe to with `.log` **Requires Administrator server permission.** **Bot Owner only.** | `.logevents`
|
||||
`.log` | Toggles logging event. Disables it if it's active anywhere on the server. Enables if it's not active. Use `.logevents` to see a list of all events you can subscribe to. **Requires Administrator server permission.** **Bot Owner only.** | `.log userpresence` or `.log userbanned`
|
||||
`.fwmsgs` | Toggles forwarding of non-command messages sent to bot's DM to the bot owners **Bot Owner only.** | `.fwmsgs`
|
||||
`.fwtoall` | Toggles whether messages will be forwarded to all bot owners or only to the first one specified in the credentials.json **Bot Owner only.** | `.fwtoall`
|
||||
`.autoassignrole` `.aar` | Automaticaly assigns a specified role to every user who joins the server. **Requires ManageRoles server permission.** | `.aar` to disable, `.aar Role Name` to enable
|
||||
`.resetperms` | Resets BOT's permissions module on this server to the default value. **Requires Administrator server permission.** | `.resetperms`
|
||||
`.delmsgoncmd` | Toggles the automatic deletion of user's successful command message to prevent chat flood. **Requires Administrator server permission.** | `.delmsgoncmd`
|
||||
`.setrole` `.sr` | Sets a role for a given user. **Requires ManageRoles server permission.** | `.sr @User Guest`
|
||||
@ -91,6 +41,57 @@ Command and aliases | Description | Usage
|
||||
`.mentionrole` `.menro` | Mentions every person from the provided role or roles (separated by a ',') on this server. Requires you to have mention everyone permission. **Requires MentionEveryone server permission.** | `.menro RoleName`
|
||||
`.donators` | List of lovely people who donated to keep this project alive. | `.donators`
|
||||
`.donadd` | Add a donator to the database. **Bot Owner only.** | `.donadd Donate Amount`
|
||||
`.autoassignrole` `.aar` | Automaticaly assigns a specified role to every user who joins the server. **Requires ManageRoles server permission.** | `.aar` to disable, `.aar Role Name` to enable
|
||||
`.fwmsgs` | Toggles forwarding of non-command messages sent to bot's DM to the bot owners **Bot Owner only.** | `.fwmsgs`
|
||||
`.fwtoall` | Toggles whether messages will be forwarded to all bot owners or only to the first one specified in the credentials.json **Bot Owner only.** | `.fwtoall`
|
||||
`.logserver` | Enables or Disables ALL log events. If enabled, all log events will log to this channel. **Requires Administrator server permission.** **Bot Owner only.** | `.logserver enable` or `.logserver disable`
|
||||
`.logignore` | Toggles whether the .logserver command ignores this channel. Useful if you have hidden admin channel and public log channel. **Requires Administrator server permission.** **Bot Owner only.** | `.logignore`
|
||||
`.logevents` | Shows a list of all events you can subscribe to with `.log` **Requires Administrator server permission.** **Bot Owner only.** | `.logevents`
|
||||
`.log` | Toggles logging event. Disables it if it's active anywhere on the server. Enables if it's not active. Use `.logevents` to see a list of all events you can subscribe to. **Requires Administrator server permission.** **Bot Owner only.** | `.log userpresence` or `.log userbanned`
|
||||
`.migratedata` | Migrate data from old bot configuration **Bot Owner only.** | `.migratedata`
|
||||
`.setmuterole` | Sets a name of the role which will be assigned to people who should be muted. Default is nadeko-mute. **Requires ManageRoles server permission.** | `.setmuterole Silenced`
|
||||
`.mute` | Mutes a mentioned user both from speaking and chatting. **Requires ManageRoles server permission.** **Requires MuteMembers server permission.** | `.mute @Someone`
|
||||
`.unmute` | Unmutes a mentioned user previously muted with `.mute` command. **Requires ManageRoles server permission.** **Requires MuteMembers server permission.** | `.unmute @Someone`
|
||||
`.chatmute` | Prevents a mentioned user from chatting in text channels. **Requires ManageRoles server permission.** | `.chatmute @Someone`
|
||||
`.chatunmute` | Removes a mute role previously set on a mentioned user with `.chatmute` which prevented him from chatting in text channels. **Requires ManageRoles server permission.** | `.chatunmute @Someone`
|
||||
`.voicemute` | Prevents a mentioned user from speaking in voice channels. **Requires MuteMembers server permission.** | `.voicemute @Someone`
|
||||
`.voiceunmute` | Gives a previously voice-muted user a permission to speak. **Requires MuteMembers server permission.** | `.voiceunmute @Someguy`
|
||||
`.rotateplaying` `.ropl` | Toggles rotation of playing status of the dynamic strings you previously specified. **Bot Owner only.** | `.ropl`
|
||||
`.addplaying` `.adpl` | Adds a specified string to the list of playing strings to rotate. Supported placeholders: %servers%, %users%, %playing%, %queued%, %time%,%shardid%,%shardcount%, %shardguilds% **Bot Owner only.** | `.adpl`
|
||||
`.listplaying` `.lipl` | Lists all playing statuses with their corresponding number. **Bot Owner only.** | `.lipl`
|
||||
`.removeplaying` `.rmpl` `.repl` | Removes a playing string on a given number. **Bot Owner only.** | `.rmpl`
|
||||
`.antiraid` | Sets an anti-raid protection on the server. First argument is number of people which will trigger the protection. Second one is a time interval in which that number of people needs to join in order to trigger the protection, and third argument is punishment for those people (Kick, Ban, Mute) **Requires Administrator server permission.** | `.antiraid 5 20 Kick`
|
||||
`.antispam` | Stops people from repeating same message X times in a row. You can specify to either mute, kick or ban the offenders. **Requires Administrator server permission.** | `.antispam 3 Mute` or `.antispam 4 Kick` or `.antispam 6 Ban`
|
||||
`.antispamignore` | Toggles whether antispam ignores current channel. Antispam must be enabled. | `.antispamignore`
|
||||
`.antilist` `.antilst` | Shows currently enabled protection features. | `.antilist`
|
||||
`.slowmode` | Toggles slowmode. Disable by specifying no parameters. To enable, specify a number of messages each user can send, and an interval in seconds. For example 1 message every 5 seconds. **Requires ManageMessages server permission.** | `.slowmode 1 5` or `.slowmode`
|
||||
`.adsarm` | Toggles the automatic deletion of confirmations for .iam and .iamn commands. **Requires ManageMessages server permission.** | `.adsarm`
|
||||
`.asar` | Adds a role to the list of self-assignable roles. **Requires ManageRoles server permission.** | `.asar Gamer`
|
||||
`.rsar` | Removes a specified role from the list of self-assignable roles. **Requires ManageRoles server permission.** | `.rsar`
|
||||
`.lsar` | Lists all self-assignable roles. | `.lsar`
|
||||
`.togglexclsar` `.tesar` | Toggles whether the self-assigned roles are exclusive. (So that any person can have only one of the self assignable roles) **Requires ManageRoles server permission.** | `.tesar`
|
||||
`.iam` | Adds a role to you that you choose. Role must be on a list of self-assignable roles. | `.iam Gamer`
|
||||
`.iamnot` `.iamn` | Removes a role to you that you choose. Role must be on a list of self-assignable roles. | `.iamn Gamer`
|
||||
`.leave` | Makes Nadeko leave the server. Either name or id required. **Bot Owner only.** | `.leave 123123123331`
|
||||
`.die` | Shuts the bot down. **Bot Owner only.** | `.die`
|
||||
`.setname` `.newnm` | Gives the bot a new name. **Bot Owner only.** | `.newnm BotName`
|
||||
`.setstatus` | Sets the bot's status. (Online/Idle/Dnd/Invisible) **Bot Owner only.** | `.setstatus Idle`
|
||||
`.setavatar` `.setav` | Sets a new avatar image for the NadekoBot. Argument is a direct link to an image. **Bot Owner only.** | `.setav http://i.imgur.com/xTG3a1I.jpg`
|
||||
`.setgame` | Sets the bots game. **Bot Owner only.** | `.setgame with snakes`
|
||||
`.setstream` | Sets the bots stream. First argument is the twitch link, second argument is stream name. **Bot Owner only.** | `.setstream TWITCHLINK Hello`
|
||||
`.send` | Sends a message to someone on a different server through the bot. Separate server and channel/user ids with `|` and prepend channel id with `c:` and user id with `u:`. **Bot Owner only.** | `.send serverid|c:channelid message` or `.send serverid|u:userid message`
|
||||
`.announce` | Sends a message to all servers' general channel bot is connected to. **Bot Owner only.** | `.announce Useless spam`
|
||||
`.reloadimages` | Reloads images bot is using. Safe to use even when bot is being used heavily. **Bot Owner only.** | `.reloadimages`
|
||||
`.greetdel` `.grdel` | Sets the time it takes (in seconds) for greet messages to be auto-deleted. Set 0 to disable automatic deletion. **Requires ManageServer server permission.** | `.greetdel 0` or `.greetdel 30`
|
||||
`.greet` | Toggles anouncements on the current channel when someone joins the server. **Requires ManageServer server permission.** | `.greet`
|
||||
`.greetmsg` | Sets a new join announcement message which will be shown in the server's channel. Type %user% if you want to mention the new member. Using it with no message will show the current greet message. **Requires ManageServer server permission.** | `.greetmsg Welcome, %user%.`
|
||||
`.greetdm` | Toggles whether the greet messages will be sent in a DM (This is separate from greet - you can have both, any or neither enabled). **Requires ManageServer server permission.** | `.greetdm`
|
||||
`.greetdmmsg` | Sets a new join announcement message which will be sent to the user who joined. Type %user% if you want to mention the new member. Using it with no message will show the current DM greet message. **Requires ManageServer server permission.** | `.greetdmmsg Welcome to the server, %user%`.
|
||||
`.bye` | Toggles anouncements on the current channel when someone leaves the server. **Requires ManageServer server permission.** | `.bye`
|
||||
`.byemsg` | Sets a new leave announcement message. Type %user% if you want to show the name the user who left. Type %id% to show id. Using this command with no message will show the current bye message. **Requires ManageServer server permission.** | `.byemsg %user% has left.`
|
||||
`.byedel` | Sets the time it takes (in seconds) for bye messages to be auto-deleted. Set 0 to disable automatic deletion. **Requires ManageServer server permission.** | `.byedel 0` or `.byedel 30`
|
||||
`.voice+text` `.v+t` | Creates a text channel for each voice channel only users in that voice channel can see.If you are server owner, keep in mind you will see them all the time regardless. **Requires ManageRoles server permission.** **Requires ManageChannels server permission.** | `.v+t`
|
||||
`.cleanvplust` `.cv+t` | Deletes all text channels ending in `-voice` for which voicechannels are not found. Use at your own risk. **Requires ManageChannels server permission.** **Requires ManageRoles server permission.** | `.cleanv+t`
|
||||
|
||||
###### [Back to TOC](#table-of-contents)
|
||||
|
||||
@ -125,24 +126,6 @@ Command and aliases | Description | Usage
|
||||
### Gambling
|
||||
Command and aliases | Description | Usage
|
||||
----------------|--------------|-------
|
||||
`$claimwaifu` `$claim` | Claim a waifu for yourself by spending currency. You must spend atleast 10% more than her current value unless she set `$affinity` towards you. | `$claim 50 @Himesama`
|
||||
`$divorce` | Releases your claim on a specific waifu. You will get some of the money you've spent back unless that waifu has an affinity towards you. 6 hours cooldown. | `$divorce @CheatingSloot`
|
||||
`$affinity` | Sets your affinity towards someone you want to be claimed by. Setting affinity will reduce their `$claim` on you by 20%. You can leave second argument empty to clear your affinity. 30 minutes cooldown. | `$affinity @MyHusband` or `$affinity`
|
||||
`$waifus` `$waifulb` | Shows top 9 waifus. | `$waifus`
|
||||
`$waifuinfo` `$waifustats` | Shows waifu stats for a target person. Defaults to you if no user is provided. | `$waifuinfo @MyCrush` or `$waifuinfo`
|
||||
`$slotstats` | Shows the total stats of the slot command for this bot's session. **Bot Owner only.** | `$slotstats`
|
||||
`$slottest` | Tests to see how much slots payout for X number of plays. **Bot Owner only.** | `$slottest 1000`
|
||||
`$slot` | Play Nadeko slots. Max bet is 999. 3 seconds cooldown per user. | `$slot 5`
|
||||
`$flip` | Flips coin(s) - heads or tails, and shows an image. | `$flip` or `$flip 3`
|
||||
`$betflip` `$bf` | Bet to guess will the result be heads or tails. Guessing awards you 1.8x the currency you've bet. | `$bf 5 heads` or `$bf 3 t`
|
||||
`$draw` | Draws a card from the deck.If you supply number X, she draws up to 5 cards from the deck. | `$draw` or `$draw 5`
|
||||
`$shuffle` `$sh` | Reshuffles all cards back into the deck. | `$sh`
|
||||
`$roll` | Rolls 0-100. If you supply a number [x] it rolls up to 30 normal dice. If you split 2 numbers with letter d (xdy) it will roll x dice from 1 to y. Y can be a letter 'F' if you want to roll fate dice instead of dnd. | `$roll` or `$roll 7` or `$roll 3d5` or `$roll 5dF`
|
||||
`$rolluo` | Rolls X normal dice (up to 30) unordered. If you split 2 numbers with letter d (xdy) it will roll x dice from 1 to y. | `$rolluo` or `$rolluo 7` or `$rolluo 3d5`
|
||||
`$nroll` | Rolls in a given range. | `$nroll 5` (rolls 0-5) or `$nroll 5-15`
|
||||
`$startevent` | Starts one of the events seen on public nadeko. **Bot Owner only.** | `$startevent flowerreaction`
|
||||
`$race` | Starts a new animal race. | `$race`
|
||||
`$joinrace` `$jr` | Joins a new race. You can specify an amount of currency for betting (optional). You will get YourBet*(participants-1) back if you win. | `$jr` or `$jr 5`
|
||||
`$raffle` | Prints a name and ID of a random user from the online list from the (optional) role. | `$raffle` or `$raffle RoleName`
|
||||
`$cash` `$$$` | Check how much currency a person has. (Defaults to yourself) | `$$$` or `$$$ @SomeGuy`
|
||||
`$give` | Give someone a certain amount of currency. | `$give 1 "@SomeGuy"`
|
||||
@ -150,36 +133,54 @@ Command and aliases | Description | Usage
|
||||
`$take` | Takes a certain amount of currency from someone. **Bot Owner only.** | `$take 1 "@someguy"`
|
||||
`$betroll` `$br` | Bets a certain amount of currency and rolls a dice. Rolling over 66 yields x2 of your currency, over 90 - x3 and 100 x10. | `$br 5`
|
||||
`$leaderboard` `$lb` | Displays bot currency leaderboard. | `$lb`
|
||||
`$race` | Starts a new animal race. | `$race`
|
||||
`$joinrace` `$jr` | Joins a new race. You can specify an amount of currency for betting (optional). You will get YourBet*(participants-1) back if you win. | `$jr` or `$jr 5`
|
||||
`$startevent` | Starts one of the events seen on public nadeko. **Bot Owner only.** | `$startevent flowerreaction`
|
||||
`$roll` | Rolls 0-100. If you supply a number [x] it rolls up to 30 normal dice. If you split 2 numbers with letter d (xdy) it will roll x dice from 1 to y. Y can be a letter 'F' if you want to roll fate dice instead of dnd. | `$roll` or `$roll 7` or `$roll 3d5` or `$roll 5dF`
|
||||
`$rolluo` | Rolls X normal dice (up to 30) unordered. If you split 2 numbers with letter d (xdy) it will roll x dice from 1 to y. | `$rolluo` or `$rolluo 7` or `$rolluo 3d5`
|
||||
`$nroll` | Rolls in a given range. | `$nroll 5` (rolls 0-5) or `$nroll 5-15`
|
||||
`$draw` | Draws a card from the deck.If you supply number X, she draws up to 5 cards from the deck. | `$draw` or `$draw 5`
|
||||
`$shuffle` `$sh` | Reshuffles all cards back into the deck. | `$sh`
|
||||
`$flip` | Flips coin(s) - heads or tails, and shows an image. | `$flip` or `$flip 3`
|
||||
`$betflip` `$bf` | Bet to guess will the result be heads or tails. Guessing awards you 1.95x the currency you've bet (rounded up). Multiplier can be changed by the bot owner. | `$bf 5 heads` or `$bf 3 t`
|
||||
`$slotstats` | Shows the total stats of the slot command for this bot's session. **Bot Owner only.** | `$slotstats`
|
||||
`$slottest` | Tests to see how much slots payout for X number of plays. **Bot Owner only.** | `$slottest 1000`
|
||||
`$slot` | Play Nadeko slots. Max bet is 999. 3 seconds cooldown per user. | `$slot 5`
|
||||
`$claimwaifu` `$claim` | Claim a waifu for yourself by spending currency. You must spend atleast 10% more than her current value unless she set `$affinity` towards you. | `$claim 50 @Himesama`
|
||||
`$divorce` | Releases your claim on a specific waifu. You will get some of the money you've spent back unless that waifu has an affinity towards you. 6 hours cooldown. | `$divorce @CheatingSloot`
|
||||
`$affinity` | Sets your affinity towards someone you want to be claimed by. Setting affinity will reduce their `$claim` on you by 20%. You can leave second argument empty to clear your affinity. 30 minutes cooldown. | `$affinity @MyHusband` or `$affinity`
|
||||
`$waifus` `$waifulb` | Shows top 9 waifus. | `$waifus`
|
||||
`$waifuinfo` `$waifustats` | Shows waifu stats for a target person. Defaults to you if no user is provided. | `$waifuinfo @MyCrush` or `$waifuinfo`
|
||||
|
||||
###### [Back to TOC](#table-of-contents)
|
||||
|
||||
### Games
|
||||
Command and aliases | Description | Usage
|
||||
----------------|--------------|-------
|
||||
`>trivia` `>t` | Starts a game of trivia. You can add nohint to prevent hints.First player to get to 10 points wins by default. You can specify a different number. 30 seconds per question. | `>t` or `>t 5 nohint`
|
||||
`>tl` | Shows a current trivia leaderboard. | `>tl`
|
||||
`>tq` | Quits current trivia after current question. | `>tq`
|
||||
`>typestart` | Starts a typing contest. | `>typestart`
|
||||
`>typestop` | Stops a typing contest on the current channel. | `>typestop`
|
||||
`>typeadd` | Adds a new article to the typing contest. **Bot Owner only.** | `>typeadd wordswords`
|
||||
`>typelist` | Lists added typing articles with their IDs. 15 per page. | `>typelist` or `>typelist 3`
|
||||
`>typedel` | Deletes a typing article given the ID. **Bot Owner only.** | `>typedel 3`
|
||||
`>poll` | Creates a poll which requires users to send the number of the voting option to the bot. **Requires ManageMessages server permission.** | `>poll Question?;Answer1;Answ 2;A_3`
|
||||
`>publicpoll` `>ppoll` | Creates a public poll which requires users to type a number of the voting option in the channel command is ran in. **Requires ManageMessages server permission.** | `>ppoll Question?;Answer1;Answ 2;A_3`
|
||||
`>pollstats` | Shows the poll results without stopping the poll on this server. **Requires ManageMessages server permission.** | `>pollstats`
|
||||
`>pollend` | Stops active poll on this server and prints the results in this channel. **Requires ManageMessages server permission.** | `>pollend`
|
||||
`>pick` | Picks the currency planted in this channel. 60 seconds cooldown. | `>pick`
|
||||
`>plant` | Spend a unit of currency to plant it in this channel. (If bot is restarted or crashes, the currency will be lost) | `>plant`
|
||||
`>gencurrency` `>gc` | Toggles currency generation on this channel. Every posted message will have chance to spawn currency. Chance is specified by the Bot Owner. (default is 2%) **Requires ManageMessages server permission.** | `>gc`
|
||||
`>hangmanlist` | Shows a list of hangman term types. | `> hangmanlist`
|
||||
`>hangman` | Starts a game of hangman in the channel. Use `>hangmanlist` to see a list of available term types. Defaults to 'all'. | `>hangman` or `>hangman movies`
|
||||
`>cleverbot` | Toggles cleverbot session. When enabled, the bot will reply to messages starting with bot mention in the server. Custom reactions starting with %mention% won't work if cleverbot is enabled. **Requires ManageMessages server permission.** | `>cleverbot`
|
||||
`>acrophobia` `>acro` | Starts an Acrophobia game. Second argment is optional round length in seconds. (default is 60) | `>acro` or `>acro 30`
|
||||
`>choose` | Chooses a thing from a list of things | `>choose Get up;Sleep;Sleep more`
|
||||
`>8ball` | Ask the 8ball a yes/no question. | `>8ball should I do something`
|
||||
`>rps` | Play a game of rocket paperclip scissors with Nadeko. | `>rps scissors`
|
||||
`>linux` | Prints a customizable Linux interjection | `>linux Spyware Windows`
|
||||
`>leet` | Converts a text to leetspeak with 6 (1-6) severity levels | `>leet 3 Hello`
|
||||
`>acrophobia` `>acro` | Starts an Acrophobia game. Second argment is optional round length in seconds. (default is 60) | `>acro` or `>acro 30`
|
||||
`>cleverbot` | Toggles cleverbot session. When enabled, the bot will reply to messages starting with bot mention in the server. Custom reactions starting with %mention% won't work if cleverbot is enabled. **Requires ManageMessages server permission.** | `>cleverbot`
|
||||
`>hangmanlist` | Shows a list of hangman term types. | `> hangmanlist`
|
||||
`>hangman` | Starts a game of hangman in the channel. Use `>hangmanlist` to see a list of available term types. Defaults to 'all'. | `>hangman` or `>hangman movies`
|
||||
`>pick` | Picks the currency planted in this channel. 60 seconds cooldown. | `>pick`
|
||||
`>plant` | Spend an amount of currency to plant it in this channel. Default is 1. (If bot is restarted or crashes, the currency will be lost) | `>plant` or `>plant 5`
|
||||
`>gencurrency` `>gc` | Toggles currency generation on this channel. Every posted message will have chance to spawn currency. Chance is specified by the Bot Owner. (default is 2%) **Requires ManageMessages server permission.** | `>gc`
|
||||
`>poll` | Creates a poll which requires users to send the number of the voting option to the bot. **Requires ManageMessages server permission.** | `>poll Question?;Answer1;Answ 2;A_3`
|
||||
`>publicpoll` `>ppoll` | Creates a public poll which requires users to type a number of the voting option in the channel command is ran in. **Requires ManageMessages server permission.** | `>ppoll Question?;Answer1;Answ 2;A_3`
|
||||
`>pollstats` | Shows the poll results without stopping the poll on this server. **Requires ManageMessages server permission.** | `>pollstats`
|
||||
`>pollend` | Stops active poll on this server and prints the results in this channel. **Requires ManageMessages server permission.** | `>pollend`
|
||||
`>typestart` | Starts a typing contest. | `>typestart`
|
||||
`>typestop` | Stops a typing contest on the current channel. | `>typestop`
|
||||
`>typeadd` | Adds a new article to the typing contest. **Bot Owner only.** | `>typeadd wordswords`
|
||||
`>typelist` | Lists added typing articles with their IDs. 15 per page. | `>typelist` or `>typelist 3`
|
||||
`>typedel` | Deletes a typing article given the ID. **Bot Owner only.** | `>typedel 3`
|
||||
`>trivia` `>t` | Starts a game of trivia. You can add nohint to prevent hints.First player to get to 10 points wins by default. You can specify a different number. 30 seconds per question. | `>t` or `>t 5 nohint`
|
||||
`>tl` | Shows a current trivia leaderboard. | `>tl`
|
||||
`>tq` | Quits current trivia after current question. | `>tq`
|
||||
|
||||
###### [Back to TOC](#table-of-contents)
|
||||
|
||||
@ -227,6 +228,7 @@ Command and aliases | Description | Usage
|
||||
`!!deleteplaylist` `!!delpls` | Deletes a saved playlist. Only if you made it or if you are the bot owner. | `!!delpls animu-5`
|
||||
`!!goto` | Goes to a specific time in seconds in a song. | `!!goto 30`
|
||||
`!!autoplay` `!!ap` | Toggles autoplay - When the song is finished, automatically queue a related youtube song. (Works only for youtube songs and when queue is empty) | `!!ap`
|
||||
`!!setmusicchannel` `!!smch` | Sets the current channel as the default music output channel. This will output playing, finished, paused and removed songs to that channel instead of the channel where the first song was queued in. **Requires ManageMessages server permission.** | `!!smch`
|
||||
|
||||
###### [Back to TOC](#table-of-contents)
|
||||
|
||||
@ -236,12 +238,12 @@ Command and aliases | Description | Usage
|
||||
`~hentai` | Shows a hentai image from a random website (gelbooru or danbooru or konachan or atfbooru or yandere) with a given tag. Tag is optional but preferred. Only 1 tag allowed. | `~hentai yuri`
|
||||
`~autohentai` | Posts a hentai every X seconds with a random tag from the provided tags. Use `|` to separate tags. 20 seconds minimum. Provide no arguments to disable. **Requires ManageMessages channel permission.** | `~autohentai 30 yuri|tail|long_hair` or `~autohentai`
|
||||
`~hentaibomb` | Shows a total 5 images (from gelbooru, danbooru, konachan, yandere and atfbooru). Tag is optional but preferred. | `~hentaibomb yuri`
|
||||
`~danbooru` | Shows a random hentai image from danbooru with a given tag. Tag is optional but preferred. (multiple tags are appended with +) | `~danbooru yuri+kissing`
|
||||
`~yandere` | Shows a random image from yandere with a given tag. Tag is optional but preferred. (multiple tags are appended with +) | `~yandere tag1+tag2`
|
||||
`~konachan` | Shows a random hentai image from konachan with a given tag. Tag is optional but preferred. | `~konachan yuri`
|
||||
`~gelbooru` | Shows a random hentai image from gelbooru with a given tag. Tag is optional but preferred. (multiple tags are appended with +) | `~gelbooru yuri+kissing`
|
||||
`~rule34` | Shows a random image from rule34.xx with a given tag. Tag is optional but preferred. (multiple tags are appended with +) | `~rule34 yuri+kissing`
|
||||
`~e621` | Shows a random hentai image from e621.net with a given tag. Tag is optional but preferred. Use spaces for multiple tags. | `~e621 yuri kissing`
|
||||
`~danbooru` | Shows a random hentai image from danbooru with a given tag. Tag is optional but preferred. (multiple tags are appended with +) | `~danbooru yuri+kissing`
|
||||
`~gelbooru` | Shows a random hentai image from gelbooru with a given tag. Tag is optional but preferred. (multiple tags are appended with +) | `~gelbooru yuri+kissing`
|
||||
`~cp` | We all know where this will lead you to. | `~cp`
|
||||
`~boobs` | Real adult content. | `~boobs`
|
||||
`~butts` `~ass` `~butt` | Real adult content. | `~butts` or `~ass`
|
||||
@ -251,18 +253,6 @@ Command and aliases | Description | Usage
|
||||
### Permissions
|
||||
Command and aliases | Description | Usage
|
||||
----------------|--------------|-------
|
||||
`;srvrfilterinv` `;sfi` | Toggles automatic deleting of invites posted in the server. Does not affect Bot Owner. | `;sfi`
|
||||
`;chnlfilterinv` `;cfi` | Toggles automatic deleting of invites posted in the channel. Does not negate the ;srvrfilterinv enabled setting. Does not affect Bot Owner. | `;cfi`
|
||||
`;srvrfilterwords` `;sfw` | Toggles automatic deleting of messages containing forbidden words on the server. Does not affect Bot Owner. | `;sfw`
|
||||
`;chnlfilterwords` `;cfw` | Toggles automatic deleting of messages containing banned words on the channel. Does not negate the ;srvrfilterwords enabled setting. Does not affect bot owner. | `;cfw`
|
||||
`;fw` | Adds or removes (if it exists) a word from the list of filtered words. Use`;sfw` or `;cfw` to toggle filtering. | `;fw poop`
|
||||
`;lstfilterwords` `;lfw` | Shows a list of filtered words. | `;lfw`
|
||||
`;cmdcosts` | Shows a list of command costs. Paginated with 9 command per page. | `;cmdcosts` or `;cmdcosts 2`
|
||||
`;cmdcooldown` `;cmdcd` | Sets a cooldown per user for a command. Set to 0 to remove the cooldown. | `;cmdcd "some cmd" 5`
|
||||
`;allcmdcooldowns` `;acmdcds` | Shows a list of all commands and their respective cooldowns. | `;acmdcds`
|
||||
`;ubl` | Either [add]s or [rem]oves a user specified by a mention or ID from a blacklist. **Bot Owner only.** | `;ubl add @SomeUser` or `;ubl rem 12312312313`
|
||||
`;cbl` | Either [add]s or [rem]oves a channel specified by an ID from a blacklist. **Bot Owner only.** | `;cbl rem 12312312312`
|
||||
`;sbl` | Either [add]s or [rem]oves a server specified by a Name or ID from a blacklist. **Bot Owner only.** | `;sbl add 12312321312` or `;sbl rem SomeTrashServer`
|
||||
`;verbose` `;v` | Sets whether to show when a command/module is blocked. | `;verbose true`
|
||||
`;permrole` `;pr` | Sets a role which can change permissions. Or supply no parameters to find out the current one. Default one is 'Nadeko'. | `;pr role`
|
||||
`;listperms` `;lp` | Lists whole permission chain with their indexes. You can specify an optional page number if there are a lot of permissions. | `;lp` or `;lp 3`
|
||||
@ -280,6 +270,18 @@ Command and aliases | Description | Usage
|
||||
`;allrolemdls` `;arm` | Enable or disable all modules for a specific role. | `;arm [enable/disable] MyRole`
|
||||
`;allusrmdls` `;aum` | Enable or disable all modules for a specific user. | `;aum enable @someone`
|
||||
`;allsrvrmdls` `;asm` | Enable or disable all modules for your server. | `;asm [enable/disable]`
|
||||
`;ubl` | Either [add]s or [rem]oves a user specified by a mention or ID from a blacklist. **Bot Owner only.** | `;ubl add @SomeUser` or `;ubl rem 12312312313`
|
||||
`;cbl` | Either [add]s or [rem]oves a channel specified by an ID from a blacklist. **Bot Owner only.** | `;cbl rem 12312312312`
|
||||
`;sbl` | Either [add]s or [rem]oves a server specified by a Name or ID from a blacklist. **Bot Owner only.** | `;sbl add 12312321312` or `;sbl rem SomeTrashServer`
|
||||
`;cmdcooldown` `;cmdcd` | Sets a cooldown per user for a command. Set to 0 to remove the cooldown. | `;cmdcd "some cmd" 5`
|
||||
`;allcmdcooldowns` `;acmdcds` | Shows a list of all commands and their respective cooldowns. | `;acmdcds`
|
||||
`;cmdcosts` | Shows a list of command costs. Paginated with 9 command per page. | `;cmdcosts` or `;cmdcosts 2`
|
||||
`;srvrfilterinv` `;sfi` | Toggles automatic deleting of invites posted in the server. Does not affect Bot Owner. | `;sfi`
|
||||
`;chnlfilterinv` `;cfi` | Toggles automatic deleting of invites posted in the channel. Does not negate the ;srvrfilterinv enabled setting. Does not affect Bot Owner. | `;cfi`
|
||||
`;srvrfilterwords` `;sfw` | Toggles automatic deleting of messages containing forbidden words on the server. Does not affect Bot Owner. | `;sfw`
|
||||
`;chnlfilterwords` `;cfw` | Toggles automatic deleting of messages containing banned words on the channel. Does not negate the ;srvrfilterwords enabled setting. Does not affect bot owner. | `;cfw`
|
||||
`;fw` | Adds or removes (if it exists) a word from the list of filtered words. Use`;sfw` or `;cfw` to toggle filtering. | `;fw poop`
|
||||
`;lstfilterwords` `;lfw` | Shows a list of filtered words. | `;lfw`
|
||||
|
||||
###### [Back to TOC](#table-of-contents)
|
||||
|
||||
@ -297,32 +299,6 @@ Command and aliases | Description | Usage
|
||||
### Searches
|
||||
Command and aliases | Description | Usage
|
||||
----------------|--------------|-------
|
||||
`~xkcd` | Shows a XKCD comic. No arguments will retrieve random one. Number argument will retrieve a specific comic, and "latest" will get the latest one. | `~xkcd` or `~xkcd 1400` or `~xkcd latest`
|
||||
`~translate` `~trans` | Translates from>to text. From the given language to the destination language. | `~trans en>fr Hello`
|
||||
`~autotrans` `~at` | Starts automatic translation of all messages by users who set their `~atl` in this channel. You can set "del" argument to automatically delete all translated user messages. **Requires Administrator server permission.** **Bot Owner only.** | `~at` or `~at del`
|
||||
`~autotranslang` `~atl` | `~atl en>fr` | Sets your source and target language to be used with `~at`. Specify no arguments to remove previously set value.
|
||||
`~translangs` | Lists the valid languages for translation. | `~translangs`
|
||||
`~hitbox` `~hb` | Notifies this channel when a certain user starts streaming. **Requires ManageMessages server permission.** | `~hitbox SomeStreamer`
|
||||
`~twitch` `~tw` | Notifies this channel when a certain user starts streaming. **Requires ManageMessages server permission.** | `~twitch SomeStreamer`
|
||||
`~beam` `~bm` | Notifies this channel when a certain user starts streaming. **Requires ManageMessages server permission.** | `~beam SomeStreamer`
|
||||
`~liststreams` `~ls` | Lists all streams you are following on this server. | `~ls`
|
||||
`~removestream` `~rms` | Removes notifications of a certain streamer from a certain platform on this channel. **Requires ManageMessages server permission.** | `~rms Twitch SomeGuy` or `~rms Beam SomeOtherGuy`
|
||||
`~checkstream` `~cs` | Checks if a user is online on a certain streaming platform. | `~cs twitch MyFavStreamer`
|
||||
`~pokemon` `~poke` | Searches for a pokemon. | `~poke Sylveon`
|
||||
`~pokemonability` `~pokeab` | Searches for a pokemon ability. | `~pokeab overgrow`
|
||||
`~placelist` | Shows the list of available tags for the `~place` command. | `~placelist`
|
||||
`~place` | Shows a placeholder image of a given tag. Use `~placelist` to see all available tags. You can specify the width and height of the image as the last two optional arguments. | `~place Cage` or `~place steven 500 400`
|
||||
`~overwatch` `~ow` | Show's basic stats on a player (competitive rank, playtime, level etc) Region codes are: `eu` `us` `cn` `kr` | `~ow us Battletag#1337` or `~overwatch eu Battletag#2016`
|
||||
`~osu` | Shows osu stats for a player. | `~osu Name` or `~osu Name taiko`
|
||||
`~osub` | Shows information about an osu beatmap. | `~osub https://osu.ppy.sh/s/127712`
|
||||
`~osu5` | Displays a user's top 5 plays. | `~osu5 Name`
|
||||
`~yomama` `~ym` | Shows a random joke from <http://api.yomomma.info/> | `~ym`
|
||||
`~randjoke` `~rj` | Shows a random joke from <http://tambal.azurewebsites.net/joke/random> | `~rj`
|
||||
`~chucknorris` `~cn` | Shows a random chucknorris joke from <http://tambal.azurewebsites.net/joke/random> | `~cn`
|
||||
`~wowjoke` | Get one of Kwoth's penultimate WoW jokes. | `~wowjoke`
|
||||
`~magicitem` `~mi` | Shows a random magicitem from <https://1d4chan.org/wiki/List_of_/tg/%27s_magic_items> | `~mi`
|
||||
`~anime` `~ani` `~aq` | Queries anilist for an anime and shows the first result. | `~ani aquarion evol`
|
||||
`~manga` `~mang` `~mq` | Queries anilist for a manga and shows the first result. | `~mq Shingeki no kyojin`
|
||||
`~weather` `~we` | Shows weather data for a specified city. You can also specify a country after a comma. | `~we Moscow, RU`
|
||||
`~youtube` `~yt` | Searches youtubes and shows the first result | `~yt query`
|
||||
`~imdb` `~omdb` | Queries omdb for movies or series, show first result. | `~imdb Batman vs Superman`
|
||||
@ -353,34 +329,40 @@ Command and aliases | Description | Usage
|
||||
`~lolban` | Shows top banned champions ordered by ban rate. | `~lolban`
|
||||
`~memelist` | Pulls a list of memes you can use with `~memegen` from http://memegen.link/templates/ | `~memelist`
|
||||
`~memegen` | Generates a meme from memelist with top and bottom text. | `~memegen biw "gets iced coffee" "in the winter"`
|
||||
`~mal` | Shows basic info from myanimelist profile. | `~mal straysocks`
|
||||
`~anime` `~ani` `~aq` | Queries anilist for an anime and shows the first result. | `~ani aquarion evol`
|
||||
`~manga` `~mang` `~mq` | Queries anilist for a manga and shows the first result. | `~mq Shingeki no kyojin`
|
||||
`~yomama` `~ym` | Shows a random joke from <http://api.yomomma.info/> | `~ym`
|
||||
`~randjoke` `~rj` | Shows a random joke from <http://tambal.azurewebsites.net/joke/random> | `~rj`
|
||||
`~chucknorris` `~cn` | Shows a random chucknorris joke from <http://tambal.azurewebsites.net/joke/random> | `~cn`
|
||||
`~wowjoke` | Get one of Kwoth's penultimate WoW jokes. | `~wowjoke`
|
||||
`~magicitem` `~mi` | Shows a random magicitem from <https://1d4chan.org/wiki/List_of_/tg/%27s_magic_items> | `~mi`
|
||||
`~osu` | Shows osu stats for a player. | `~osu Name` or `~osu Name taiko`
|
||||
`~osub` | Shows information about an osu beatmap. | `~osub https://osu.ppy.sh/s/127712`
|
||||
`~osu5` | Displays a user's top 5 plays. | `~osu5 Name`
|
||||
`~overwatch` `~ow` | Show's basic stats on a player (competitive rank, playtime, level etc) Region codes are: `eu` `us` `cn` `kr` | `~ow us Battletag#1337` or `~overwatch eu Battletag#2016`
|
||||
`~placelist` | Shows the list of available tags for the `~place` command. | `~placelist`
|
||||
`~place` | Shows a placeholder image of a given tag. Use `~placelist` to see all available tags. You can specify the width and height of the image as the last two optional arguments. | `~place Cage` or `~place steven 500 400`
|
||||
`~pokemon` `~poke` | Searches for a pokemon. | `~poke Sylveon`
|
||||
`~pokemonability` `~pokeab` | Searches for a pokemon ability. | `~pokeab overgrow`
|
||||
`~hitbox` `~hb` | Notifies this channel when a certain user starts streaming. **Requires ManageMessages server permission.** | `~hitbox SomeStreamer`
|
||||
`~twitch` `~tw` | Notifies this channel when a certain user starts streaming. **Requires ManageMessages server permission.** | `~twitch SomeStreamer`
|
||||
`~beam` `~bm` | Notifies this channel when a certain user starts streaming. **Requires ManageMessages server permission.** | `~beam SomeStreamer`
|
||||
`~liststreams` `~ls` | Lists all streams you are following on this server. | `~ls`
|
||||
`~removestream` `~rms` | Removes notifications of a certain streamer from a certain platform on this channel. **Requires ManageMessages server permission.** | `~rms Twitch SomeGuy` or `~rms Beam SomeOtherGuy`
|
||||
`~checkstream` `~cs` | Checks if a user is online on a certain streaming platform. | `~cs twitch MyFavStreamer`
|
||||
`~translate` `~trans` | Translates from>to text. From the given language to the destination language. | `~trans en>fr Hello`
|
||||
`~autotrans` `~at` | Starts automatic translation of all messages by users who set their `~atl` in this channel. You can set "del" argument to automatically delete all translated user messages. **Requires Administrator server permission.** **Bot Owner only.** | `~at` or `~at del`
|
||||
`~autotranslang` `~atl` | `~atl en>fr` | Sets your source and target language to be used with `~at`. Specify no arguments to remove previously set value.
|
||||
`~translangs` | Lists the valid languages for translation. | `~translangs`
|
||||
`~xkcd` | Shows a XKCD comic. No arguments will retrieve random one. Number argument will retrieve a specific comic, and "latest" will get the latest one. | `~xkcd` or `~xkcd 1400` or `~xkcd latest`
|
||||
|
||||
###### [Back to TOC](#table-of-contents)
|
||||
|
||||
### Utility
|
||||
Command and aliases | Description | Usage
|
||||
----------------|--------------|-------
|
||||
`.convertlist` | List of the convertible dimensions and currencies. | `.convertlist`
|
||||
`.convert` | Convert quantities. Use `.convertlist` to see supported dimensions and currencies. | `.convert m km 1000`
|
||||
`.remind` | Sends a message to you or a channel after certain amount of time. First argument is me/here/'channelname'. Second argument is time in a descending order (mo>w>d>h>m) example: 1w5d3h10m. Third argument is a (multiword)message. | `.remind me 1d5h Do something` or `.remind #general 1m Start now!`
|
||||
`.remindtemplate` | Sets message for when the remind is triggered. Available placeholders are %user% - user who ran the command, %message% - Message specified in the remind, %target% - target channel of the remind. **Bot Owner only.** | `.remindtemplate %user%, do %message%!`
|
||||
`.listquotes` `.liqu` | `.liqu` or `.liqu 3` | Lists all quotes on the server ordered alphabetically. 15 Per page.
|
||||
`...` | Shows a random quote with a specified name. | `... abc`
|
||||
`..` | Adds a new quote with the specified name and message. | `.. sayhi Hi`
|
||||
`.deletequote` `.delq` | Deletes a random quote with the specified keyword. You have to either be server Administrator or the creator of the quote to delete it. | `.delq abc`
|
||||
`.delallq` `.daq` | Deletes all quotes on a specified keyword. **Requires Administrator server permission.** | `.delallq kek`
|
||||
`.repeatinvoke` `.repinv` | Immediately shows the repeat message on a certain index and restarts its timer. **Requires ManageMessages server permission.** | `.repinv 1`
|
||||
`.repeatremove` `.reprm` | Removes a repeating message on a specified index. Use `.repeatlist` to see indexes. **Requires ManageMessages server permission.** | `.reprm 2`
|
||||
`.repeat` | Repeat a message every X minutes in the current channel. You can have up to 5 repeating messages on the server in total. **Requires ManageMessages server permission.** | `.repeat 5 Hello there`
|
||||
`.repeatlist` `.replst` | Shows currently repeating messages and their indexes. **Requires ManageMessages server permission.** | `.repeatlist`
|
||||
`.serverinfo` `.sinfo` | Shows info about the server the bot is on. If no channel is supplied, it defaults to current one. | `.sinfo Some Server`
|
||||
`.channelinfo` `.cinfo` | Shows info about the channel. If no channel is supplied, it defaults to current one. | `.cinfo #some-channel`
|
||||
`.userinfo` `.uinfo` | Shows info about the user. If no user is supplied, it defaults a user running the command. | `.uinfo @SomeUser`
|
||||
`.scsc` | Starts an instance of cross server channel. You will get a token as a DM that other people will use to tune in to the same instance. **Bot Owner only.** | `.scsc`
|
||||
`.jcsc` | Joins current channel to an instance of cross server channel using the token. **Requires ManageServer server permission.** | `.jcsc TokenHere`
|
||||
`.lcsc` | Leaves Cross server channel instance from this channel. **Requires ManageServer server permission.** | `.lcsc`
|
||||
`.calculate` `.calc` | Evaluate a mathematical expression. | `.calc 1+1`
|
||||
`.calcops` | Shows all available operations in .calc command | `.calcops`
|
||||
`.rotaterolecolor` `.rrc` | Rotates a roles color on an interval with a list of supplied colors. First argument is interval in seconds (Minimum 60). Second argument is a role, followed by a space-separated list of colors in hex. Provide a rolename with a 0 interval to disable. **Bot Owner only.** | `.rrc 60 MyLsdRole #ff0000 #00ff00 #0000ff` or `.rrc 0 MyLsdRole`
|
||||
`.rotaterolecolor` `.rrc` | Rotates a roles color on an interval with a list of supplied colors. First argument is interval in seconds (Minimum 60). Second argument is a role, followed by a space-separated list of colors in hex. Provide a rolename with a 0 interval to disable. **Requires ManageRoles server permission.** **Bot Owner only.** | `.rrc 60 MyLsdRole #ff0000 #00ff00 #0000ff` or `.rrc 0 MyLsdRole`
|
||||
`.togethertube` `.totube` | Creates a new room on <https://togethertube.com> and shows the link in the chat. | `.totube`
|
||||
`.whosplaying` `.whpl` | Shows a list of users who are playing the specified game. | `.whpl Overwatch`
|
||||
`.inrole` | Lists every person from the provided role or roles (separated by a ',') on this server. If the list is too long for 1 message, you must have Manage Messages permission. | `.inrole Role`
|
||||
@ -396,3 +378,24 @@ Command and aliases | Description | Usage
|
||||
`.listservers` | Lists servers the bot is on with some basic info. 15 per page. **Bot Owner only.** | `.listservers 3`
|
||||
`.savechat` | Saves a number of messages to a text file and sends it to you. **Bot Owner only.** | `.savechat 150`
|
||||
`.activity` | Checks for spammers. **Bot Owner only.** | `.activity`
|
||||
`.calculate` `.calc` | Evaluate a mathematical expression. | `.calc 1+1`
|
||||
`.calcops` | Shows all available operations in .calc command | `.calcops`
|
||||
`.scsc` | Starts an instance of cross server channel. You will get a token as a DM that other people will use to tune in to the same instance. **Bot Owner only.** | `.scsc`
|
||||
`.jcsc` | Joins current channel to an instance of cross server channel using the token. **Requires ManageServer server permission.** | `.jcsc TokenHere`
|
||||
`.lcsc` | Leaves Cross server channel instance from this channel. **Requires ManageServer server permission.** | `.lcsc`
|
||||
`.serverinfo` `.sinfo` | Shows info about the server the bot is on. If no channel is supplied, it defaults to current one. | `.sinfo Some Server`
|
||||
`.channelinfo` `.cinfo` | Shows info about the channel. If no channel is supplied, it defaults to current one. | `.cinfo #some-channel`
|
||||
`.userinfo` `.uinfo` | Shows info about the user. If no user is supplied, it defaults a user running the command. | `.uinfo @SomeUser`
|
||||
`.repeatinvoke` `.repinv` | Immediately shows the repeat message on a certain index and restarts its timer. **Requires ManageMessages server permission.** | `.repinv 1`
|
||||
`.repeatremove` `.reprm` | Removes a repeating message on a specified index. Use `.repeatlist` to see indexes. **Requires ManageMessages server permission.** | `.reprm 2`
|
||||
`.repeat` | Repeat a message every X minutes in the current channel. You can have up to 5 repeating messages on the server in total. **Requires ManageMessages server permission.** | `.repeat 5 Hello there`
|
||||
`.repeatlist` `.replst` | Shows currently repeating messages and their indexes. **Requires ManageMessages server permission.** | `.repeatlist`
|
||||
`.listquotes` `.liqu` | `.liqu` or `.liqu 3` | Lists all quotes on the server ordered alphabetically. 15 Per page.
|
||||
`...` | Shows a random quote with a specified name. | `... abc`
|
||||
`..` | Adds a new quote with the specified name and message. | `.. sayhi Hi`
|
||||
`.deletequote` `.delq` | Deletes a random quote with the specified keyword. You have to either be server Administrator or the creator of the quote to delete it. | `.delq abc`
|
||||
`.delallq` `.daq` | Deletes all quotes on a specified keyword. **Requires Administrator server permission.** | `.delallq kek`
|
||||
`.remind` | Sends a message to you or a channel after certain amount of time. First argument is me/here/'channelname'. Second argument is time in a descending order (mo>w>d>h>m) example: 1w5d3h10m. Third argument is a (multiword)message. | `.remind me 1d5h Do something` or `.remind #general 1m Start now!`
|
||||
`.remindtemplate` | Sets message for when the remind is triggered. Available placeholders are %user% - user who ran the command, %message% - Message specified in the remind, %target% - target channel of the remind. **Bot Owner only.** | `.remindtemplate %user%, do %message%!`
|
||||
`.convertlist` | List of the convertible dimensions and currencies. | `.convertlist`
|
||||
`.convert` | Convert quantities. Use `.convertlist` to see supported dimensions and currencies. | `.convert m km 1000`
|
||||
|
@ -19,7 +19,7 @@
|
||||
|
||||
###Question 5: I have an issue/bug/suggestion, where do I put it so it gets noticed?
|
||||
-----------
|
||||
**Answer:** First, check [issues](https://github.com/Kwoth/NadekoBot/issues "GitHub NadekoBot Issues"), then check the `#suggestions` channel in the Nadeko [help server](https://discord.gg/0ehQwTK2RBjAxzEY).
|
||||
**Answer:** First, check [issues](https://github.com/Kwoth/NadekoBot/issues "GitHub NadekoBot Issues"), then check the `#suggestions` channel in the Nadeko [help server](https://discord.gg/nadekobot).
|
||||
|
||||
If your problem or suggestion is not there, feel free to request/notify us about it either in the Issues section of GitHub for issues or in the `#suggestions` channel on the Nadeko help server for suggestions.
|
||||
|
||||
|
@ -8,11 +8,11 @@ Assuming you have followed the link above to setup an account and Droplet with 6
|
||||
|
||||
**Go through this whole guide before setting up Nadeko**
|
||||
|
||||
#### Prerequisites
|
||||
####Prerequisites
|
||||
- Download [PuTTY](http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html)
|
||||
- Download [CyberDuck](https://cyberduck.io) or [WinSCP](https://winscp.net/eng/download.php)
|
||||
- Download [WinSCP](https://winscp.net/eng/download.php) *(optional)*
|
||||
|
||||
#### Follow these steps
|
||||
####Starting up
|
||||
|
||||
- **Open PuTTY.exe** that you downloaded before, and paste or enter your `IP address` and then click **Open**.
|
||||
If you entered your Droplets IP address correctly, it should show **login as:** in a newly opened window.
|
||||
@ -24,7 +24,162 @@ If you entered your Droplets IP address correctly, it should show **login as:**
|
||||
|
||||
**NOTE:** Copy the commands, and just paste them using **mouse single right-click.**
|
||||
|
||||
####Installing Git
|
||||
####Creating and Inviting bot
|
||||
|
||||
- Read here how to [create a DiscordBot application](http://nadekobot.readthedocs.io/en/latest/guides/Windows%20Guide/#creating-discordbot-application)
|
||||
- [Visual Invite Guide](http://discord.kongslien.net/guide.html) **(Note: Client ID is your Bot ID)**
|
||||
- Copy your `Client ID` from your [applications page](https://discordapp.com/developers/applications/me).
|
||||
- Replace the **12345678** in this link:
|
||||
`https://discordapp.com/oauth2/authorize?client_id=`12345678`&scope=bot&permissions=66186303`
|
||||
with your `Client ID`
|
||||
- The link should now look like this:
|
||||
`https://discordapp.com/oauth2/authorize?client_id=`**YOUR_CLENT_ID_HERE**`&scope=bot&permissions=66186303`
|
||||
- Go to the newly created link and pick the server we created, and click `Authorize`
|
||||
- The bot should have been added to your server.
|
||||
|
||||
####Getting NadekoBot
|
||||
#####Part I
|
||||
Use the following command to get and run `linuxAIO.sh`
|
||||
(Remember **Do Not** rename the file **linuxAIO.sh**)
|
||||
|
||||
`cd ~ && wget -N https://github.com/Kwoth/NadekoBot-BashScript/raw/master/linuxAIO.sh && bash linuxAIO.sh`
|
||||
|
||||
You should see these following options after using the above command:
|
||||
|
||||
```
|
||||
1. Download Dev Build (Latest)
|
||||
2. Download Stable Build
|
||||
3. Run Nadeko (Normally)
|
||||
4. Run Nadeko with Auto Restart (Run Nadeko normally before using this.)
|
||||
5. Auto-Install Prerequisites (for Ubuntu, Debian and CentOS)
|
||||
6. Set up credentials.json (if you have downloaded the bot already)
|
||||
7. To exit
|
||||
```
|
||||
#####Part II (Optional)
|
||||
**If** you are running NadekoBot for the first time on your system and never had any *prerequisites* installed and have Ubuntu, Debian or CentOS, Press `5` and `enter` key, then `y` when you see the following:
|
||||
```
|
||||
Welcome to NadekoBot Auto Prerequisites Installer.
|
||||
Would you like to continue?
|
||||
```
|
||||
That will install all the prerequisites your system need to run NadekoBot.
|
||||
|
||||
If you prefer to install them [manually](http://nadekobot.readthedocs.io/en/latest/guides/Linux%20Guide/#installing-manually-optional), click on the link. *(Optional)*
|
||||
|
||||
Once *prerequisites* finish installing.
|
||||
#####Part III
|
||||
Choose either
|
||||
`1` to get the **most updated build of NadekoBot**
|
||||
or
|
||||
`2` to get the **previously stable build of NadekoBot**
|
||||
and then press `enter` key.
|
||||
|
||||
Once Installation is completed you should see the options again.
|
||||
|
||||
Next, check out:
|
||||
#####Part IV (Optional)
|
||||
If you prefer to skip this step and want to do it [manually](http://nadekobot.readthedocs.io/en/latest/guides/Linux%20Guide/#setting-up-sftp) or already have the `credentials.json` file, click on the link. *(Optional)*
|
||||
|
||||
- [1. Setting up credentials.json](http://nadekobot.readthedocs.io/en/latest/guides/Linux%20Guide/#setting-up-credentialsjson)
|
||||
- [2. To Get the Google API](http://nadekobot.readthedocs.io/en/latest/guides/Windows%20Guide/#setting-up-nadekobot-for-music)
|
||||
- [3. JSON Explanations for other APIs](http://nadekobot.readthedocs.io/en/latest/JSON%20Explanations/)
|
||||
|
||||
You will need the following for the next step:
|
||||
![botimg](https://cdn.discordapp.com/attachments/251504306010849280/276455844223123457/Capture.PNG)
|
||||
|
||||
- **Bot's Client ID** and **Bot's ID** (both are same) [(*required)](http://nadekobot.readthedocs.io/en/latest/guides/Windows%20Guide/#setting-up-credentialsjson-file)
|
||||
- **Bot's Token** (not client secret) [(*required)](http://nadekobot.readthedocs.io/en/latest/guides/Windows%20Guide/#setting-up-credentialsjson-file)
|
||||
- Your **Discord userID** [(*required)](http://nadekobot.readthedocs.io/en/latest/guides/Windows%20Guide/#setting-up-credentialsjson-file)
|
||||
- **Google Api Key** [(optional)](http://nadekobot.readthedocs.io/en/latest/guides/Windows%20Guide/#setting-up-nadekobot-for-music)
|
||||
- **LoL Api Key** [(optional)](http://nadekobot.readthedocs.io/en/latest/JSON%20Explanations/)
|
||||
- **Mashape Key** [(optional)](http://nadekobot.readthedocs.io/en/latest/JSON%20Explanations/)
|
||||
- **Osu Api Key** [(optional)](http://nadekobot.readthedocs.io/en/latest/JSON%20Explanations/)
|
||||
- **Sound Cloud Client Id** [(optional)](http://nadekobot.readthedocs.io/en/latest/JSON%20Explanations/)
|
||||
|
||||
Once you have acquired them, press `6` to **Set up credentials.json**
|
||||
|
||||
You will be asked to enter the required informations, just follow the on-screen instructions and enter the required information.
|
||||
*i.e* If you are asked **Bot's Token**, then just copy and paste or type the **Bot's Token** and press `enter` key.
|
||||
|
||||
(If you want to skip any optional infos, just press `enter` key without typing/pasting anything.)
|
||||
Once done,
|
||||
#####Part V
|
||||
You should see the options again.
|
||||
Next, press `3` to **Run Nadeko (Normally)**
|
||||
Check in your discord server if your new bot is working properly.
|
||||
#####Part VI
|
||||
If your bot is working properly in your server, type `.die` to **shut down the bot**, then press `7` to **exit**.
|
||||
Next, [Run your bot again with **tmux**.](http://nadekobot.readthedocs.io/en/latest/guides/Linux%20Guide/#running-nadekobot)
|
||||
|
||||
[Check this when you need to **restart** your **NadekoBot** anytime later along with tmux session.](http://nadekobot.readthedocs.io/en/latest/guides/Linux%20Guide/#restarting-nadeko)
|
||||
|
||||
####Running NadekoBot
|
||||
|
||||
**Create a new Session:**
|
||||
|
||||
- `tmux new -s nadeko`
|
||||
|
||||
The above command will create a new session named **nadeko** *(you can replace “nadeko” with anything you prefer and remember its your session name)* so you can run the bot in background without having to keep the PuTTY running.
|
||||
|
||||
**Next, we need to run `linuxAIO.sh` in order to get the latest running scripts with patches:**
|
||||
|
||||
- `cd ~ && bash linuxAIO.sh`
|
||||
|
||||
**From the options,**
|
||||
|
||||
Choose `3` to **Run NadekoBot normally.**
|
||||
**NOTE:** With option `3` (Running Normally), if you use `.die` [command](http://nadekobot.readthedocs.io/en/latest/Commands%20List/#administration) in discord. The bot will shut down and will stay offline until you manually run it again. (best if you want to check the bot.)
|
||||
|
||||
Choose `4` to **Run NadekoBot with Auto Restart.**
|
||||
It will show you more options:
|
||||
```
|
||||
1. Run Auto Restart normally without Updating.
|
||||
2. Auto Restart and Update with Dev Build (latest)
|
||||
3. Auto Restart and Update with Stable Build
|
||||
4. Exit
|
||||
```
|
||||
**NOTE:** With option `4` (Running with Auto Restart), bot will auto run if you use `.die` [command](http://nadekobot.readthedocs.io/en/latest/Commands%20List/#administration) making the command `.die` to function as restart.
|
||||
|
||||
See how that happens:
|
||||
|
||||
![img9](https://cdn.discordapp.com/attachments/251504306010849280/251506312893038592/die_explaination.gif)
|
||||
|
||||
**Remember** that, while running with Auto Restart, you will need to [close the tmux session](http://nadekobot.readthedocs.io/en/latest/guides/Linux%20Guide/#restarting-nadeko) to stop the bot completely.
|
||||
|
||||
**Now check your Discord, the bot should be online**
|
||||
|
||||
Next to **move the bot to background** and to do that, press **CTRL+B+D** (that will detach the nadeko session using TMUX) and you can finally close **PuTTY** if you want.
|
||||
|
||||
####Restarting Nadeko
|
||||
|
||||
**Restarting NadekoBot:**
|
||||
|
||||
**If** you have chosen option `4` to **Run Nadeko with Auto Restart** from Nadeko's `linuxAIO.sh` *[(you got it from this step)](http://nadekobot.readthedocs.io/en/latest/guides/Linux%20Guide/#getting-nadekobot)*
|
||||
You can simply type `.die` in the server you have your NadekoBot to make her restart.
|
||||
|
||||
**Restarting Nadeko with the Server:**
|
||||
|
||||
Open **PuTTY** and login as you have before, type `reboot` and hit Enter.
|
||||
|
||||
**Restarting Manually:**
|
||||
|
||||
- Kill your previous session, check with `tmux ls`
|
||||
- `tmux kill-session -t nadeko` (don't forget to replace "nadeko" to what ever you named your bot's session)
|
||||
- [Run the bot again.](http://nadekobot.readthedocs.io/en/latest/guides/Linux%20Guide/#running-nadekobot)
|
||||
|
||||
####Updating Nadeko
|
||||
|
||||
- Connect to the terminal through **PuTTY**.
|
||||
- `tmux kill-session -t nadeko` (don't forget to replace **nadeko** in the command with the name of your bot's session)
|
||||
- Make sure the bot is **not** running.
|
||||
- `tmux new -s nadeko` (**nadeko** is the name of the session)
|
||||
- `cd ~ && bash linuxAIO.sh`
|
||||
- Choose either `1` or `2` to update the bot with **latest build** or **stable build** respectively.
|
||||
- Choose either `3` or `4` to run the bot again with **normally** or **auto restart** respectively.
|
||||
- Done. You can close **PuTTY** now.
|
||||
|
||||
####Installing Manually (Optional)
|
||||
|
||||
#####Installing Git
|
||||
|
||||
![img1](https://cdn.discordapp.com/attachments/251504306010849280/251504416019054592/git.gif)
|
||||
|
||||
@ -38,14 +193,14 @@ CentOS:
|
||||
|
||||
**NOTE:** If the command is not being initiated, hit **Enter**
|
||||
|
||||
####Installing .NET Core SDK
|
||||
#####Installing .NET Core SDK
|
||||
|
||||
![img2](https://cdn.discordapp.com/attachments/251504306010849280/251504746987388938/dotnet.gif)
|
||||
|
||||
Go to [this link](https://www.microsoft.com/net/core#ubuntu) (for Ubuntu) or to [this link](https://www.microsoft.com/net/core#linuxcentos) (for CentOS) provided by microsoft for instructions on how to get the most up to date version of the dotnet core sdk!
|
||||
Make sure that you're on the correct page for your distribution of linux as the guides are different for the various distributions
|
||||
|
||||
We'll go over the steps here for Ubuntu 16.04 anyway (these will **only** work on Ubuntu 16.04), accurate as of 25/11/2016
|
||||
We'll go over the steps here for Ubuntu 16.04 anyway (these will **only** work on Ubuntu 16.04), accurate as of 3/2/2017
|
||||
|
||||
```
|
||||
sudo sh -c 'echo "deb [arch=amd64] https://apt-mo.trafficmanager.net/repos/dotnet-release/ xenial main" > /etc/apt/sources.list.d/dotnetdev.list'
|
||||
@ -55,7 +210,7 @@ sudo apt-get update && sudo apt-get install dotnet-dev-1.0.0-preview2.1-003177 -
|
||||
|
||||
**NOTE:** .NET CORE SDK only supports 64-bit Linux Operating Systems (Raspberry Pis are not supported because of this)
|
||||
|
||||
####Installing Opus Voice Codec and libsodium
|
||||
#####Installing Opus Voice Codec and libsodium
|
||||
|
||||
![img3](https://cdn.discordapp.com/attachments/251504306010849280/251505294654308353/libopus.gif)
|
||||
|
||||
@ -67,7 +222,7 @@ CentOS:
|
||||
|
||||
`yum -y install opus opus-devel`
|
||||
|
||||
####Installing FFMPEG
|
||||
#####Installing FFMPEG
|
||||
|
||||
![img4](https://cdn.discordapp.com/attachments/251504306010849280/251505443111829505/ffmpeg.gif)
|
||||
|
||||
@ -101,7 +256,7 @@ echo "deb http://ftp.debian.org/debian jessie-backports main" | tee /etc/apt/sou
|
||||
sudo apt-get update && sudo apt-get install ffmpeg -y
|
||||
```
|
||||
|
||||
####Installing TMUX
|
||||
#####Installing TMUX
|
||||
|
||||
![img5](https://cdn.discordapp.com/attachments/251504306010849280/251505519758409728/tmux.gif)
|
||||
|
||||
@ -113,33 +268,7 @@ Centos:
|
||||
|
||||
`yum -y install tmux`
|
||||
|
||||
####Getting NadekoBot
|
||||
|
||||
Use the following command to get and run `linuxAIO.sh`:
|
||||
(Remember **DO NOT** rename the file `linuxAIO.sh`)
|
||||
|
||||
`cd ~ && wget -N https://github.com/Kwoth/NadekoBot-BashScript/raw/master/linuxAIO.sh && bash linuxAIO.sh`
|
||||
|
||||
Follow the on screen instructions:
|
||||
|
||||
1. To Get the latest build. (most recent updates)
|
||||
2. To Get the stable build.
|
||||
|
||||
Choose either `1` or `2` then press `enter` key.
|
||||
Once Installation is completed you should see the options again.
|
||||
Next, choose `5` to exit.
|
||||
|
||||
####Creating and Inviting bot
|
||||
|
||||
- Read here how to [create a DiscordBot application](http://nadekobot.readthedocs.io/en/latest/guides/Windows%20Guide/#creating-discordbot-application)
|
||||
- [Visual Invite Guide](http://discord.kongslien.net/guide.html) *NOTE: Client ID is your Bot ID*
|
||||
- Copy your `Client ID` from your [applications page](https://discordapp.com/developers/applications/me).
|
||||
- Replace the `12345678` in this link `https://discordapp.com/oauth2/authorize?client_id=12345678&scope=bot&permissions=66186303` with your `Client ID`.
|
||||
- The link should now look like this: `https://discordapp.com/oauth2/authorize?client_id=**YOUR_CLENT_ID_HERE**&scope=bot&permissions=66186303`.
|
||||
- Go to the newly created link and pick the server we created, and click `Authorize`.
|
||||
- The bot should have been added to your server.
|
||||
|
||||
####Guide for Advance Users
|
||||
####Guide for Advance Users (Optional)
|
||||
|
||||
**Skip this step if you are a Regular User or New to Linux.**
|
||||
|
||||
@ -158,23 +287,23 @@ Next, choose `5` to exit.
|
||||
|
||||
####Setting up SFTP
|
||||
|
||||
- Open **CyberDuck**
|
||||
- Click on **Open Connection** (top-left corner), a new window should appear.
|
||||
- You should see **FTP (File Transfer Protocol)** in drop-down.
|
||||
- Change it to **SFTP (SSH File Transfer Protocol)**
|
||||
- Now, in **Server:** paste or type in your `Digital Ocean Droplets IP address`, leave `Port: 22` (no need to change it)
|
||||
- Open **WinSCP**
|
||||
- Click on **New Site** (top-left corner).
|
||||
- On the right-hand side, you should see **File Protocol** above a drop-down selection menu.
|
||||
- Select **SFTP** *(SSH File Transfer Protocol)* if its not already selected.
|
||||
- Now, in **Host name:** paste or type in your `Digital Ocean Droplets IP address` and leave `Port: 22` (no need to change it).
|
||||
- In **Username:** type `root`
|
||||
- In **Password:** type `the new root password (you changed at the start)`
|
||||
- Click on **Connect**
|
||||
- It should show you the NadekoBot folder which was created by git earlier
|
||||
- Click on **Login**, it should connect.
|
||||
- It should show you the NadekoBot folder which was created by git earlier on the right-hand side window.
|
||||
- Open that folder, then open the `src` folder, followed by another `NadekoBot` folder and you should see `credentials.json` there.
|
||||
|
||||
####Setting up credentials.json
|
||||
|
||||
- Copy the `credentials.json` to desktop
|
||||
- EDIT it as it is guided here: [Setting up credentials.json](http://nadekobot.readthedocs.io/en/latest/guides/Windows%20Guide/#setting-up-credentialsjson-file)
|
||||
- Paste/put it back in the folder once done. `(Using CyberDuck/WinSCP)`
|
||||
- **If** you already have Nadeko 1.0 setup and have `credentials.json` and `NadekoBot.db`, you can just copy and paste the `credentials.json` to `NadekoBot/src/NadekoBot` and `NadekoBot.db` to `NadekoBot/src/NadekoBot/bin/Release/netcoreapp1.0/data` using CyberDuck.
|
||||
- Paste/put it back in the folder once done. `(Using WinSCP)`
|
||||
- **If** you already have Nadeko 1.0 setup and have `credentials.json` and `NadekoBot.db`, you can just copy and paste the `credentials.json` to `NadekoBot/src/NadekoBot` and `NadekoBot.db` to `NadekoBot/src/NadekoBot/bin/Release/netcoreapp1.0/data` using WinSCP.
|
||||
- **If** you have Nadeko 0.9x follow the [Upgrading Guide](http://nadekobot.readthedocs.io/en/latest/guides/Upgrading%20Guide/)
|
||||
|
||||
####Setting up Music
|
||||
@ -183,80 +312,18 @@ To set up Nadeko for music and Google API Keys, follow [Setting up NadekoBot for
|
||||
|
||||
Once done, go back to **PuTTY**
|
||||
|
||||
####Running NadekoBot
|
||||
####Some more Info
|
||||
|
||||
**Create a new Session:**
|
||||
|
||||
- `tmux new -s nadeko`
|
||||
|
||||
The above command will create a new session named **nadeko** *(you can replace “nadeko” with anything you prefer and remember its your session name)* so you can run the bot in background without having to keep the PuTTY running.
|
||||
|
||||
**Next, we need to run `linuxAIO.sh` in order to get the latest running scripts with patches:**
|
||||
|
||||
- `cd ~ && bash linuxAIO.sh`
|
||||
|
||||
From the options,
|
||||
|
||||
Choose `3` To Run the bot normally.
|
||||
**NOTE:** With option `3` (Running Normally), if you use `.die` [command](http://nadekobot.readthedocs.io/en/latest/Commands%20List/#administration) in discord. The bot will shut down and will stay offline until you manually run it again. (best if you want to check the bot.)
|
||||
|
||||
Choose `4` To Run the bot with Auto Restart.
|
||||
**NOTE:** With option `4` (Running with Auto Restart), bot will auto run if you use `.die` [command](http://nadekobot.readthedocs.io/en/latest/Commands%20List/#administration) making the command `.die` to function as restart.
|
||||
|
||||
See how that happens:
|
||||
|
||||
![img9](https://cdn.discordapp.com/attachments/251504306010849280/251506312893038592/die_explaination.gif)
|
||||
|
||||
**Remember** that, while running with Auto Restart, you will need to [close the tmux session](http://nadekobot.readthedocs.io/en/latest/guides/Linux%20Guide/#restarting-nadeko) to stop the bot completely.
|
||||
|
||||
**Now check your Discord, the bot should be online**
|
||||
|
||||
Next to **move the bot to background** and to do that, press **CTRL+B+D** (this will detach the nadeko session using TMUX), and you can finally close PuTTY now.
|
||||
|
||||
####Some more Info (just in case)
|
||||
|
||||
**Info about tmux:**
|
||||
#####Info about tmux
|
||||
|
||||
- If you want to **see the sessions** after logging back again, type `tmux ls`, and that will give you the list of sessions running.
|
||||
- If you want to **switch to/ see that session**, type `tmux a -t nadeko` (**nadeko** is the name of the session we created before so, replace **“nadeko”** with the session name you created.)
|
||||
- If you want to **kill** NadekoBot **session**, type `tmux kill-session -t nadeko`
|
||||
|
||||
**If you are running Ubuntu 16.10, and having trouble installing .NET Core:**
|
||||
|
||||
- Go to [Download Page for libicu55_55.1-7_amd64.deb](http://packages.ubuntu.com/en/xenial/amd64/libicu55/download)
|
||||
- Copy the link with a download option closest to you
|
||||
- `wget <copied link>` *e.g.* `wget http://mirrors.kernel.org/ubuntu/pool/main/i/icu/libicu55_55.1-7_amd64.deb` (make sure it is downloaded)
|
||||
- Install with: `dpkg –i libicu55_55.1-7_amd64.deb`
|
||||
- Now go back and install the .NET Core
|
||||
|
||||
####Restarting Nadeko
|
||||
|
||||
**Restarting Nadeko with the Server:**
|
||||
|
||||
Open **PuTTY** and login as you have before, type `reboot` and hit Enter.
|
||||
|
||||
**Restarting Manually:**
|
||||
|
||||
- Kill your previous session, check with `tmux ls`
|
||||
- `tmux kill-session -t nadeko` (don't forget to replace "nadeko" to what ever you named your bot's session)
|
||||
- [Run the bot again.](http://nadekobot.readthedocs.io/en/latest/guides/Linux%20Guide/#running-nadekobot)
|
||||
|
||||
####Updating Nadeko
|
||||
|
||||
- Connect to the terminal through PuTTY.
|
||||
- `tmux kill-session -t nadeko` (don't forget to replace **nadeko** in the command with the name of your bot's session)
|
||||
- Make sure the bot is **not** running.
|
||||
- `tmux new -s nadeko` (**nadeko** is the name of the session)
|
||||
- `cd ~ && bash linuxAIO.sh`
|
||||
- Choose either `1` or `2` to update the bot with **latest build** or **stable build** respectively.
|
||||
- Choose either `3` or `4` to run the bot again with **normally** or **auto restart** respectively.
|
||||
- Done. You can close PuTTY now.
|
||||
|
||||
####Alternative way to Install
|
||||
#####Alternative way to Install
|
||||
|
||||
If the [Nadeko installer](http://nadekobot.readthedocs.io/en/latest/guides/Linux%20Guide/#getting-nadekobot) shows any kind error, check if you have the `linuxAIO.sh` file and make sure its not renamed or if you want to manually install the bot. Use the following command(s):
|
||||
|
||||
|
||||
![img6](https://cdn.discordapp.com/attachments/251504306010849280/251505587089571850/getting_nadeko.gif)
|
||||
|
||||
`cd ~ && curl -L https://github.com/Kwoth/NadekoBot-BashScript/raw/master/nadeko_installer.sh | sh`
|
||||
@ -264,7 +331,7 @@ If the [Nadeko installer](http://nadekobot.readthedocs.io/en/latest/guides/Linux
|
||||
**OR**
|
||||
|
||||
```
|
||||
cd ~ && git clone -b 1.0 --recursive --depth 1 https://github.com/Kwoth/NadekoBot.git
|
||||
cd ~ && git clone -b dev --recursive --depth 1 https://github.com/Kwoth/NadekoBot.git
|
||||
cd ~/NadekoBot/discord.net/src/Discord.Net && dotnet restore && cd ../Discord.Net.Commands && dotnet restore && cd ../../../src/NadekoBot/ && dotnet restore && dotnet build --configuration Release
|
||||
```
|
||||
|
||||
|
@ -33,7 +33,7 @@ If you want to contribute, be sure to PR on the **[dev][dev]** branch.
|
||||
- [Donate](Donate.md)
|
||||
|
||||
[img]: https://cdn.discordapp.com/attachments/202743183774318593/210580315381563392/discord.png
|
||||
[NadekoBot Server]: https://discord.gg/0ehQwTK2RBjAxzEY
|
||||
[NadekoBot Server]: https://discord.gg/nadekobot
|
||||
[GitHub]: https://github.com/Kwoth/NadekoBot
|
||||
[Issues]: https://github.com/Kwoth/NadekoBot/issues
|
||||
[dev]: https://github.com/Kwoth/NadekoBot/tree/dev
|
||||
|
23
src/NadekoBot/DataStructures/AsyncLazy.cs
Normal file
23
src/NadekoBot/DataStructures/AsyncLazy.cs
Normal file
@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NadekoBot.DataStructures
|
||||
{
|
||||
public class AsyncLazy<T> : Lazy<Task<T>>
|
||||
{
|
||||
public AsyncLazy(Func<T> valueFactory) :
|
||||
base(() => Task.Factory.StartNew(valueFactory))
|
||||
{ }
|
||||
|
||||
public AsyncLazy(Func<Task<T>> taskFactory) :
|
||||
base(() => Task.Factory.StartNew(taskFactory).Unwrap())
|
||||
{ }
|
||||
|
||||
public TaskAwaiter<T> GetAwaiter() { return Value.GetAwaiter(); }
|
||||
}
|
||||
|
||||
}
|
91
src/NadekoBot/DataStructures/CREmbed.cs
Normal file
91
src/NadekoBot/DataStructures/CREmbed.cs
Normal file
@ -0,0 +1,91 @@
|
||||
using Discord;
|
||||
using Newtonsoft.Json;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NadekoBot.DataStructures
|
||||
{
|
||||
public class CREmbed
|
||||
{
|
||||
private static readonly Logger _log;
|
||||
public string PlainText { get; set; }
|
||||
public string Title { get; set; }
|
||||
public string Description { get; set; }
|
||||
public CREmbedFooter Footer { get; set; }
|
||||
public string Thumbnail { get; set; }
|
||||
public string Image { get; set; }
|
||||
public CREmbedField[] Fields { get; set; }
|
||||
public uint Color { get; set; } = 7458112;
|
||||
|
||||
static CREmbed()
|
||||
{
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
}
|
||||
|
||||
public bool IsValid =>
|
||||
!string.IsNullOrWhiteSpace(Title) ||
|
||||
!string.IsNullOrWhiteSpace(Description) ||
|
||||
!string.IsNullOrWhiteSpace(Thumbnail) ||
|
||||
!string.IsNullOrWhiteSpace(Image) ||
|
||||
(Footer != null && (!string.IsNullOrWhiteSpace(Footer.Text) || !string.IsNullOrWhiteSpace(Footer.IconUrl))) ||
|
||||
(Fields != null && Fields.Length > 0);
|
||||
|
||||
public EmbedBuilder ToEmbed()
|
||||
{
|
||||
var embed = new EmbedBuilder()
|
||||
.WithTitle(Title)
|
||||
.WithDescription(Description)
|
||||
.WithColor(new Discord.Color(Color));
|
||||
if (Footer != null)
|
||||
embed.WithFooter(efb => efb.WithIconUrl(Footer.IconUrl).WithText(Footer.Text));
|
||||
embed.WithThumbnailUrl(Thumbnail)
|
||||
.WithImageUrl(Image);
|
||||
|
||||
if (Fields != null)
|
||||
foreach (var f in Fields)
|
||||
{
|
||||
embed.AddField(efb => efb.WithName(f.Name).WithValue(f.Value).WithIsInline(f.Inline));
|
||||
}
|
||||
|
||||
return embed;
|
||||
}
|
||||
|
||||
public static bool TryParse(string input, out CREmbed embed)
|
||||
{
|
||||
embed = null;
|
||||
if (string.IsNullOrWhiteSpace(input))
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
var crembed = JsonConvert.DeserializeObject<CREmbed>(input);
|
||||
|
||||
if (!crembed.IsValid)
|
||||
return false;
|
||||
|
||||
embed = crembed;
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class CREmbedField
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Value { get; set; }
|
||||
public bool Inline { get; set; }
|
||||
}
|
||||
|
||||
public class CREmbedFooter {
|
||||
public string Text { get; set; }
|
||||
public string IconUrl { get; set; }
|
||||
}
|
||||
}
|
82
src/NadekoBot/DataStructures/DisposableImutableList.cs
Normal file
82
src/NadekoBot/DataStructures/DisposableImutableList.cs
Normal file
@ -0,0 +1,82 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NadekoBot.DataStructures
|
||||
{
|
||||
|
||||
public static class DisposableReadOnlyListExtensions
|
||||
{
|
||||
public static IDisposableReadOnlyList<T> AsDisposable<T>(this IReadOnlyList<T> arr) where T : IDisposable
|
||||
=> new DisposableReadOnlyList<T>(arr);
|
||||
|
||||
public static IDisposableReadOnlyList<KeyValuePair<TKey, TValue>> AsDisposable<TKey, TValue>(this IReadOnlyList<KeyValuePair<TKey, TValue>> arr) where TValue : IDisposable
|
||||
=> new DisposableReadOnlyList<TKey, TValue>(arr);
|
||||
}
|
||||
|
||||
public interface IDisposableReadOnlyList<T> : IReadOnlyList<T>, IDisposable
|
||||
{
|
||||
}
|
||||
|
||||
public class DisposableReadOnlyList<T> : IDisposableReadOnlyList<T>
|
||||
where T : IDisposable
|
||||
{
|
||||
private readonly IReadOnlyList<T> _arr;
|
||||
|
||||
public int Count => _arr.Count;
|
||||
|
||||
public T this[int index] => _arr[index];
|
||||
|
||||
public DisposableReadOnlyList(IReadOnlyList<T> arr)
|
||||
{
|
||||
this._arr = arr;
|
||||
}
|
||||
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
=> _arr.GetEnumerator();
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
=> _arr.GetEnumerator();
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var item in _arr)
|
||||
{
|
||||
item.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class DisposableReadOnlyList<T, U> : IDisposableReadOnlyList<KeyValuePair<T, U>>
|
||||
where U : IDisposable
|
||||
{
|
||||
private readonly IReadOnlyList<KeyValuePair<T, U>> _arr;
|
||||
|
||||
public int Count => _arr.Count;
|
||||
|
||||
KeyValuePair<T, U> IReadOnlyList<KeyValuePair<T, U>>.this[int index] => _arr[index];
|
||||
|
||||
public DisposableReadOnlyList(IReadOnlyList<KeyValuePair<T, U>> arr)
|
||||
{
|
||||
this._arr = arr;
|
||||
}
|
||||
|
||||
public IEnumerator<KeyValuePair<T, U>> GetEnumerator() =>
|
||||
_arr.GetEnumerator();
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() =>
|
||||
_arr.GetEnumerator();
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var item in _arr)
|
||||
{
|
||||
item.Value.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
1095
src/NadekoBot/Migrations/20170213164350_guild-timezone-and-locale.Designer.cs
generated
Normal file
1095
src/NadekoBot/Migrations/20170213164350_guild-timezone-and-locale.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace NadekoBot.Migrations
|
||||
{
|
||||
public partial class guildtimezoneandlocale : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Locale",
|
||||
table: "GuildConfigs",
|
||||
nullable: true,
|
||||
defaultValue: null);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "TimeZoneId",
|
||||
table: "GuildConfigs",
|
||||
nullable: true,
|
||||
defaultValue: null);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Locale",
|
||||
table: "BotConfig",
|
||||
nullable: true,
|
||||
defaultValue: null);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Locale",
|
||||
table: "GuildConfigs");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "TimeZoneId",
|
||||
table: "GuildConfigs");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Locale",
|
||||
table: "BotConfig");
|
||||
}
|
||||
}
|
||||
}
|
1171
src/NadekoBot/Migrations/20170222162505_dateadded.Designer.cs
generated
Normal file
1171
src/NadekoBot/Migrations/20170222162505_dateadded.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
357
src/NadekoBot/Migrations/20170222162505_dateadded.cs
Normal file
357
src/NadekoBot/Migrations/20170222162505_dateadded.cs
Normal file
@ -0,0 +1,357 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace NadekoBot.Migrations
|
||||
{
|
||||
public partial class dateadded : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "DateAdded",
|
||||
table: "WaifuUpdates",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "DateAdded",
|
||||
table: "WaifuInfo",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "DateAdded",
|
||||
table: "PokeGame",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "DateAdded",
|
||||
table: "SelfAssignableRoles",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "DateAdded",
|
||||
table: "Reminders",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "DateAdded",
|
||||
table: "RaceAnimals",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "DateAdded",
|
||||
table: "Quotes",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "DateAdded",
|
||||
table: "PlaylistSong",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "DateAdded",
|
||||
table: "PlayingStatus",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "DateAdded",
|
||||
table: "Permission",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "DateAdded",
|
||||
table: "MutedUserId",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "DateAdded",
|
||||
table: "MusicPlaylists",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "DateAdded",
|
||||
table: "ModulePrefixes",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "DateAdded",
|
||||
table: "LogSettings",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "DateAdded",
|
||||
table: "IgnoredVoicePresenceCHannels",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "DateAdded",
|
||||
table: "IgnoredLogChannels",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "DateAdded",
|
||||
table: "GuildRepeater",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "DateAdded",
|
||||
table: "GuildConfigs",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "DateAdded",
|
||||
table: "GCChannelId",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "DateAdded",
|
||||
table: "FollowedStream",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "DateAdded",
|
||||
table: "FilteredWord",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "DateAdded",
|
||||
table: "FilterChannelId",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "DateAdded",
|
||||
table: "EightBallResponses",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "DateAdded",
|
||||
table: "Donators",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "DateAdded",
|
||||
table: "DiscordUser",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "DateAdded",
|
||||
table: "CustomReactions",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "DateAdded",
|
||||
table: "CurrencyTransactions",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "DateAdded",
|
||||
table: "Currency",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "DateAdded",
|
||||
table: "ConversionUnits",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "DateAdded",
|
||||
table: "CommandPrice",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "DateAdded",
|
||||
table: "CommandCooldown",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "DateAdded",
|
||||
table: "ClashOfClans",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "DateAdded",
|
||||
table: "ClashCallers",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "DateAdded",
|
||||
table: "BotConfig",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "DateAdded",
|
||||
table: "BlacklistItem",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "DateAdded",
|
||||
table: "AntiSpamSetting",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "DateAdded",
|
||||
table: "AntiSpamIgnore",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "DateAdded",
|
||||
table: "AntiRaidSetting",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DateAdded",
|
||||
table: "WaifuUpdates");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DateAdded",
|
||||
table: "WaifuInfo");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DateAdded",
|
||||
table: "PokeGame");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DateAdded",
|
||||
table: "SelfAssignableRoles");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DateAdded",
|
||||
table: "Reminders");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DateAdded",
|
||||
table: "RaceAnimals");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DateAdded",
|
||||
table: "Quotes");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DateAdded",
|
||||
table: "PlaylistSong");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DateAdded",
|
||||
table: "PlayingStatus");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DateAdded",
|
||||
table: "Permission");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DateAdded",
|
||||
table: "MutedUserId");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DateAdded",
|
||||
table: "MusicPlaylists");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DateAdded",
|
||||
table: "ModulePrefixes");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DateAdded",
|
||||
table: "LogSettings");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DateAdded",
|
||||
table: "IgnoredVoicePresenceCHannels");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DateAdded",
|
||||
table: "IgnoredLogChannels");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DateAdded",
|
||||
table: "GuildRepeater");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DateAdded",
|
||||
table: "GuildConfigs");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DateAdded",
|
||||
table: "GCChannelId");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DateAdded",
|
||||
table: "FollowedStream");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DateAdded",
|
||||
table: "FilteredWord");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DateAdded",
|
||||
table: "FilterChannelId");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DateAdded",
|
||||
table: "EightBallResponses");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DateAdded",
|
||||
table: "Donators");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DateAdded",
|
||||
table: "DiscordUser");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DateAdded",
|
||||
table: "CustomReactions");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DateAdded",
|
||||
table: "CurrencyTransactions");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DateAdded",
|
||||
table: "Currency");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DateAdded",
|
||||
table: "ConversionUnits");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DateAdded",
|
||||
table: "CommandPrice");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DateAdded",
|
||||
table: "CommandCooldown");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DateAdded",
|
||||
table: "ClashOfClans");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DateAdded",
|
||||
table: "ClashCallers");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DateAdded",
|
||||
table: "BotConfig");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DateAdded",
|
||||
table: "BlacklistItem");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DateAdded",
|
||||
table: "AntiSpamSetting");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DateAdded",
|
||||
table: "AntiSpamIgnore");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DateAdded",
|
||||
table: "AntiRaidSetting");
|
||||
}
|
||||
}
|
||||
}
|
@ -24,6 +24,8 @@ namespace NadekoBot.Migrations
|
||||
|
||||
b.Property<int>("Action");
|
||||
|
||||
b.Property<DateTime?>("DateAdded");
|
||||
|
||||
b.Property<int>("GuildConfigId");
|
||||
|
||||
b.Property<int>("Seconds");
|
||||
@ -47,6 +49,8 @@ namespace NadekoBot.Migrations
|
||||
|
||||
b.Property<ulong>("ChannelId");
|
||||
|
||||
b.Property<DateTime?>("DateAdded");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("AntiSpamSettingId");
|
||||
@ -61,6 +65,8 @@ namespace NadekoBot.Migrations
|
||||
|
||||
b.Property<int>("Action");
|
||||
|
||||
b.Property<DateTime?>("DateAdded");
|
||||
|
||||
b.Property<int>("GuildConfigId");
|
||||
|
||||
b.Property<int>("MessageThreshold");
|
||||
@ -80,6 +86,8 @@ namespace NadekoBot.Migrations
|
||||
|
||||
b.Property<int?>("BotConfigId");
|
||||
|
||||
b.Property<DateTime?>("DateAdded");
|
||||
|
||||
b.Property<ulong>("ItemId");
|
||||
|
||||
b.Property<int>("Type");
|
||||
@ -120,6 +128,8 @@ namespace NadekoBot.Migrations
|
||||
|
||||
b.Property<string>("DMHelpString");
|
||||
|
||||
b.Property<DateTime?>("DateAdded");
|
||||
|
||||
b.Property<string>("ErrorColor");
|
||||
|
||||
b.Property<bool>("ForwardMessages");
|
||||
@ -128,6 +138,8 @@ namespace NadekoBot.Migrations
|
||||
|
||||
b.Property<string>("HelpString");
|
||||
|
||||
b.Property<string>("Locale");
|
||||
|
||||
b.Property<int>("MigrationVersion");
|
||||
|
||||
b.Property<int>("MinimumBetAmount");
|
||||
@ -156,6 +168,8 @@ namespace NadekoBot.Migrations
|
||||
|
||||
b.Property<int>("ClashWarId");
|
||||
|
||||
b.Property<DateTime?>("DateAdded");
|
||||
|
||||
b.Property<int?>("SequenceNumber");
|
||||
|
||||
b.Property<int>("Stars");
|
||||
@ -176,6 +190,8 @@ namespace NadekoBot.Migrations
|
||||
|
||||
b.Property<ulong>("ChannelId");
|
||||
|
||||
b.Property<DateTime?>("DateAdded");
|
||||
|
||||
b.Property<string>("EnemyClan");
|
||||
|
||||
b.Property<ulong>("GuildId");
|
||||
@ -198,6 +214,8 @@ namespace NadekoBot.Migrations
|
||||
|
||||
b.Property<string>("CommandName");
|
||||
|
||||
b.Property<DateTime?>("DateAdded");
|
||||
|
||||
b.Property<int?>("GuildConfigId");
|
||||
|
||||
b.Property<int>("Seconds");
|
||||
@ -218,6 +236,8 @@ namespace NadekoBot.Migrations
|
||||
|
||||
b.Property<string>("CommandName");
|
||||
|
||||
b.Property<DateTime?>("DateAdded");
|
||||
|
||||
b.Property<int>("Price");
|
||||
|
||||
b.HasKey("Id");
|
||||
@ -235,6 +255,8 @@ namespace NadekoBot.Migrations
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<DateTime?>("DateAdded");
|
||||
|
||||
b.Property<string>("InternalTrigger");
|
||||
|
||||
b.Property<decimal>("Modifier");
|
||||
@ -253,6 +275,8 @@ namespace NadekoBot.Migrations
|
||||
|
||||
b.Property<long>("Amount");
|
||||
|
||||
b.Property<DateTime?>("DateAdded");
|
||||
|
||||
b.Property<ulong>("UserId");
|
||||
|
||||
b.HasKey("Id");
|
||||
@ -270,6 +294,8 @@ namespace NadekoBot.Migrations
|
||||
|
||||
b.Property<long>("Amount");
|
||||
|
||||
b.Property<DateTime?>("DateAdded");
|
||||
|
||||
b.Property<string>("Reason");
|
||||
|
||||
b.Property<ulong>("UserId");
|
||||
@ -284,6 +310,8 @@ namespace NadekoBot.Migrations
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<DateTime?>("DateAdded");
|
||||
|
||||
b.Property<ulong?>("GuildId");
|
||||
|
||||
b.Property<bool>("IsRegex");
|
||||
@ -306,6 +334,8 @@ namespace NadekoBot.Migrations
|
||||
|
||||
b.Property<string>("AvatarId");
|
||||
|
||||
b.Property<DateTime?>("DateAdded");
|
||||
|
||||
b.Property<string>("Discriminator");
|
||||
|
||||
b.Property<ulong>("UserId");
|
||||
@ -326,6 +356,8 @@ namespace NadekoBot.Migrations
|
||||
|
||||
b.Property<int>("Amount");
|
||||
|
||||
b.Property<DateTime?>("DateAdded");
|
||||
|
||||
b.Property<string>("Name");
|
||||
|
||||
b.Property<ulong>("UserId");
|
||||
@ -345,6 +377,8 @@ namespace NadekoBot.Migrations
|
||||
|
||||
b.Property<int?>("BotConfigId");
|
||||
|
||||
b.Property<DateTime?>("DateAdded");
|
||||
|
||||
b.Property<string>("Text");
|
||||
|
||||
b.HasKey("Id");
|
||||
@ -361,6 +395,8 @@ namespace NadekoBot.Migrations
|
||||
|
||||
b.Property<ulong>("ChannelId");
|
||||
|
||||
b.Property<DateTime?>("DateAdded");
|
||||
|
||||
b.Property<int?>("GuildConfigId");
|
||||
|
||||
b.Property<int?>("GuildConfigId1");
|
||||
@ -379,6 +415,8 @@ namespace NadekoBot.Migrations
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<DateTime?>("DateAdded");
|
||||
|
||||
b.Property<int?>("GuildConfigId");
|
||||
|
||||
b.Property<string>("Word");
|
||||
@ -397,6 +435,8 @@ namespace NadekoBot.Migrations
|
||||
|
||||
b.Property<ulong>("ChannelId");
|
||||
|
||||
b.Property<DateTime?>("DateAdded");
|
||||
|
||||
b.Property<int?>("GuildConfigId");
|
||||
|
||||
b.Property<ulong>("GuildId");
|
||||
@ -419,6 +459,8 @@ namespace NadekoBot.Migrations
|
||||
|
||||
b.Property<ulong>("ChannelId");
|
||||
|
||||
b.Property<DateTime?>("DateAdded");
|
||||
|
||||
b.Property<int?>("GuildConfigId");
|
||||
|
||||
b.HasKey("Id");
|
||||
@ -453,6 +495,8 @@ namespace NadekoBot.Migrations
|
||||
|
||||
b.Property<bool>("CleverbotEnabled");
|
||||
|
||||
b.Property<DateTime?>("DateAdded");
|
||||
|
||||
b.Property<float>("DefaultMusicVolume");
|
||||
|
||||
b.Property<bool>("DeleteMessageOnCommand");
|
||||
@ -469,6 +513,8 @@ namespace NadekoBot.Migrations
|
||||
|
||||
b.Property<ulong>("GuildId");
|
||||
|
||||
b.Property<string>("Locale");
|
||||
|
||||
b.Property<int?>("LogSettingId");
|
||||
|
||||
b.Property<string>("MuteRoleName");
|
||||
@ -483,6 +529,8 @@ namespace NadekoBot.Migrations
|
||||
|
||||
b.Property<bool>("SendDmGreetMessage");
|
||||
|
||||
b.Property<string>("TimeZoneId");
|
||||
|
||||
b.Property<bool>("VerbosePermissions");
|
||||
|
||||
b.Property<bool>("VoicePlusTextEnabled");
|
||||
@ -506,6 +554,8 @@ namespace NadekoBot.Migrations
|
||||
|
||||
b.Property<ulong>("ChannelId");
|
||||
|
||||
b.Property<DateTime?>("DateAdded");
|
||||
|
||||
b.Property<int?>("GuildConfigId");
|
||||
|
||||
b.Property<ulong>("GuildId");
|
||||
@ -528,6 +578,8 @@ namespace NadekoBot.Migrations
|
||||
|
||||
b.Property<ulong>("ChannelId");
|
||||
|
||||
b.Property<DateTime?>("DateAdded");
|
||||
|
||||
b.Property<int?>("LogSettingId");
|
||||
|
||||
b.HasKey("Id");
|
||||
@ -544,6 +596,8 @@ namespace NadekoBot.Migrations
|
||||
|
||||
b.Property<ulong>("ChannelId");
|
||||
|
||||
b.Property<DateTime?>("DateAdded");
|
||||
|
||||
b.Property<int?>("LogSettingId");
|
||||
|
||||
b.HasKey("Id");
|
||||
@ -572,6 +626,8 @@ namespace NadekoBot.Migrations
|
||||
|
||||
b.Property<ulong?>("ChannelUpdatedId");
|
||||
|
||||
b.Property<DateTime?>("DateAdded");
|
||||
|
||||
b.Property<bool>("IsLogging");
|
||||
|
||||
b.Property<ulong?>("LogOtherId");
|
||||
@ -632,6 +688,8 @@ namespace NadekoBot.Migrations
|
||||
|
||||
b.Property<int?>("BotConfigId");
|
||||
|
||||
b.Property<DateTime?>("DateAdded");
|
||||
|
||||
b.Property<string>("ModuleName");
|
||||
|
||||
b.Property<string>("Prefix");
|
||||
@ -652,6 +710,8 @@ namespace NadekoBot.Migrations
|
||||
|
||||
b.Property<ulong>("AuthorId");
|
||||
|
||||
b.Property<DateTime?>("DateAdded");
|
||||
|
||||
b.Property<string>("Name");
|
||||
|
||||
b.HasKey("Id");
|
||||
@ -664,6 +724,8 @@ namespace NadekoBot.Migrations
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<DateTime?>("DateAdded");
|
||||
|
||||
b.Property<int?>("GuildConfigId");
|
||||
|
||||
b.Property<ulong>("UserId");
|
||||
@ -680,6 +742,8 @@ namespace NadekoBot.Migrations
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<DateTime?>("DateAdded");
|
||||
|
||||
b.Property<int?>("NextId");
|
||||
|
||||
b.Property<int>("PrimaryTarget");
|
||||
@ -707,6 +771,8 @@ namespace NadekoBot.Migrations
|
||||
|
||||
b.Property<int?>("BotConfigId");
|
||||
|
||||
b.Property<DateTime?>("DateAdded");
|
||||
|
||||
b.Property<string>("Status");
|
||||
|
||||
b.HasKey("Id");
|
||||
@ -721,6 +787,8 @@ namespace NadekoBot.Migrations
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<DateTime?>("DateAdded");
|
||||
|
||||
b.Property<int?>("MusicPlaylistId");
|
||||
|
||||
b.Property<string>("Provider");
|
||||
@ -750,6 +818,8 @@ namespace NadekoBot.Migrations
|
||||
b.Property<string>("AuthorName")
|
||||
.IsRequired();
|
||||
|
||||
b.Property<DateTime?>("DateAdded");
|
||||
|
||||
b.Property<ulong>("GuildId");
|
||||
|
||||
b.Property<string>("Keyword")
|
||||
@ -770,6 +840,8 @@ namespace NadekoBot.Migrations
|
||||
|
||||
b.Property<int?>("BotConfigId");
|
||||
|
||||
b.Property<DateTime?>("DateAdded");
|
||||
|
||||
b.Property<string>("Icon");
|
||||
|
||||
b.Property<string>("Name");
|
||||
@ -788,6 +860,8 @@ namespace NadekoBot.Migrations
|
||||
|
||||
b.Property<ulong>("ChannelId");
|
||||
|
||||
b.Property<DateTime?>("DateAdded");
|
||||
|
||||
b.Property<bool>("IsPrivate");
|
||||
|
||||
b.Property<string>("Message");
|
||||
@ -808,6 +882,8 @@ namespace NadekoBot.Migrations
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<DateTime?>("DateAdded");
|
||||
|
||||
b.Property<ulong>("GuildId");
|
||||
|
||||
b.Property<ulong>("RoleId");
|
||||
@ -825,6 +901,8 @@ namespace NadekoBot.Migrations
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<DateTime?>("DateAdded");
|
||||
|
||||
b.Property<ulong>("UserId");
|
||||
|
||||
b.Property<string>("type");
|
||||
@ -846,6 +924,8 @@ namespace NadekoBot.Migrations
|
||||
|
||||
b.Property<int?>("ClaimerId");
|
||||
|
||||
b.Property<DateTime?>("DateAdded");
|
||||
|
||||
b.Property<int>("Price");
|
||||
|
||||
b.Property<int>("WaifuId");
|
||||
@ -867,6 +947,8 @@ namespace NadekoBot.Migrations
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<DateTime?>("DateAdded");
|
||||
|
||||
b.Property<int?>("NewId");
|
||||
|
||||
b.Property<int?>("OldId");
|
||||
|
@ -1,18 +1,14 @@
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using NadekoBot.Extensions;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using NadekoBot.Services;
|
||||
using NadekoBot.Attributes;
|
||||
using Discord.WebSocket;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using System.Net.Http;
|
||||
using System.IO;
|
||||
using static NadekoBot.Modules.Permissions.Permissions;
|
||||
using System.Collections.Concurrent;
|
||||
using NLog;
|
||||
@ -20,38 +16,39 @@ using NLog;
|
||||
namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
[NadekoModule("Administration", ".")]
|
||||
public partial class Administration : DiscordModule
|
||||
public partial class Administration : NadekoTopLevelModule
|
||||
{
|
||||
private static ConcurrentHashSet<ulong> deleteMessagesOnCommand { get; }
|
||||
|
||||
private static ConcurrentDictionary<ulong, string> GuildMuteRoles { get; } = new ConcurrentDictionary<ulong, string>();
|
||||
|
||||
private static ConcurrentHashSet<ulong> DeleteMessagesOnCommand { get; } = new ConcurrentHashSet<ulong>();
|
||||
|
||||
private new static Logger _log { get; }
|
||||
private new static readonly Logger _log;
|
||||
|
||||
static Administration()
|
||||
{
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
NadekoBot.CommandHandler.CommandExecuted += DelMsgOnCmd_Handler;
|
||||
|
||||
DeleteMessagesOnCommand = new ConcurrentHashSet<ulong>(NadekoBot.AllGuildConfigs.Where(g => g.DeleteMessageOnCommand).Select(g => g.GuildId));
|
||||
deleteMessagesOnCommand = new ConcurrentHashSet<ulong>(NadekoBot.AllGuildConfigs.Where(g => g.DeleteMessageOnCommand).Select(g => g.GuildId));
|
||||
|
||||
}
|
||||
|
||||
private static async Task DelMsgOnCmd_Handler(SocketUserMessage msg, CommandInfo cmd)
|
||||
private static Task DelMsgOnCmd_Handler(SocketUserMessage msg, CommandInfo cmd)
|
||||
{
|
||||
try
|
||||
var _ = Task.Run(async () =>
|
||||
{
|
||||
var channel = msg.Channel as SocketTextChannel;
|
||||
if (channel == null)
|
||||
return;
|
||||
if (DeleteMessagesOnCommand.Contains(channel.Guild.Id) && cmd.Name != "prune")
|
||||
await msg.DeleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.Warn(ex, "Delmsgoncmd errored...");
|
||||
}
|
||||
try
|
||||
{
|
||||
var channel = msg.Channel as SocketTextChannel;
|
||||
if (channel == null)
|
||||
return;
|
||||
if (deleteMessagesOnCommand.Contains(channel.Guild.Id) && cmd.Name != "prune")
|
||||
await msg.DeleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.Warn(ex, "Delmsgoncmd errored...");
|
||||
}
|
||||
});
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -70,17 +67,17 @@ namespace NadekoBot.Modules.Administration
|
||||
PermRole = config.PermissionRole,
|
||||
Verbose = config.VerbosePermissions,
|
||||
};
|
||||
Permissions.Permissions.Cache.AddOrUpdate(channel.Guild.Id,
|
||||
Cache.AddOrUpdate(channel.Guild.Id,
|
||||
toAdd, (id, old) => toAdd);
|
||||
await uow.CompleteAsync();
|
||||
}
|
||||
|
||||
await channel.SendConfirmAsync($"{Context.Message.Author.Mention} 🆗 **Permissions for this server are reset.**");
|
||||
await ReplyConfirmLocalized("perms_reset").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[RequireUserPermission(GuildPermission.Administrator)]
|
||||
[RequireBotPermission(GuildPermission.ManageMessages)]
|
||||
public async Task Delmsgoncmd()
|
||||
{
|
||||
bool enabled;
|
||||
@ -93,29 +90,31 @@ namespace NadekoBot.Modules.Administration
|
||||
}
|
||||
if (enabled)
|
||||
{
|
||||
DeleteMessagesOnCommand.Add(Context.Guild.Id);
|
||||
await Context.Channel.SendConfirmAsync("✅ **Now automatically deleting successful command invokations.**").ConfigureAwait(false);
|
||||
deleteMessagesOnCommand.Add(Context.Guild.Id);
|
||||
await ReplyConfirmLocalized("delmsg_on").ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
DeleteMessagesOnCommand.TryRemove(Context.Guild.Id);
|
||||
await Context.Channel.SendConfirmAsync("❗**Stopped automatic deletion of successful command invokations.**").ConfigureAwait(false);
|
||||
deleteMessagesOnCommand.TryRemove(Context.Guild.Id);
|
||||
await ReplyConfirmLocalized("delmsg_off").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[RequireUserPermission(GuildPermission.ManageRoles)]
|
||||
[RequireBotPermission(GuildPermission.ManageRoles)]
|
||||
public async Task Setrole(IGuildUser usr, [Remainder] IRole role)
|
||||
{
|
||||
try
|
||||
{
|
||||
await usr.AddRolesAsync(role).ConfigureAwait(false);
|
||||
await Context.Channel.SendConfirmAsync($"ℹ️ Successfully added role **{role.Name}** to user **{usr.Username}**").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("setrole", Format.Bold(role.Name), Format.Bold(usr.ToString()))
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("⚠️ Failed to add role. **Bot has insufficient permissions.**\n").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("setrole_err").ConfigureAwait(false);
|
||||
Console.WriteLine(ex.ToString());
|
||||
}
|
||||
}
|
||||
@ -123,95 +122,94 @@ namespace NadekoBot.Modules.Administration
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[RequireUserPermission(GuildPermission.ManageRoles)]
|
||||
[RequireBotPermission(GuildPermission.ManageRoles)]
|
||||
public async Task Removerole(IGuildUser usr, [Remainder] IRole role)
|
||||
{
|
||||
try
|
||||
{
|
||||
await usr.RemoveRolesAsync(role).ConfigureAwait(false);
|
||||
await Context.Channel.SendConfirmAsync($"ℹ️ Successfully removed role **{role.Name}** from user **{usr.Username}**").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("remrole", Format.Bold(role.Name), Format.Bold(usr.ToString())).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("⚠️ Failed to remove role. Most likely reason: **Insufficient permissions.**").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("remrole_err").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[RequireUserPermission(GuildPermission.ManageRoles)]
|
||||
[RequireBotPermission(GuildPermission.ManageRoles)]
|
||||
public async Task RenameRole(IRole roleToEdit, string newname)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (roleToEdit.Position > (await Context.Guild.GetCurrentUserAsync().ConfigureAwait(false)).GetRoles().Max(r => r.Position))
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("🚫 You can't edit roles higher than your highest role.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("renrole_perms").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
await roleToEdit.ModifyAsync(g => g.Name = newname).ConfigureAwait(false);
|
||||
await Context.Channel.SendConfirmAsync("✅ Role renamed.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("renrole").ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("⚠️ Failed to rename role. Probably **insufficient permissions.**").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("renrole_err").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[RequireUserPermission(GuildPermission.ManageRoles)]
|
||||
[RequireBotPermission(GuildPermission.ManageRoles)]
|
||||
public async Task RemoveAllRoles([Remainder] IGuildUser user)
|
||||
{
|
||||
try
|
||||
{
|
||||
await user.RemoveRolesAsync(user.GetRoles()).ConfigureAwait(false);
|
||||
await Context.Channel.SendConfirmAsync($"🗑 Successfully removed **all** roles from user **{user.Username}**").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("rar", Format.Bold(user.ToString())).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("⚠️ Failed to remove roles. Most likely reason: **Insufficient permissions.**").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("rar_err").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[RequireUserPermission(GuildPermission.ManageRoles)]
|
||||
[RequireBotPermission(GuildPermission.ManageRoles)]
|
||||
public async Task CreateRole([Remainder] string roleName = null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(roleName))
|
||||
return;
|
||||
try
|
||||
{
|
||||
var r = await Context.Guild.CreateRoleAsync(roleName).ConfigureAwait(false);
|
||||
await Context.Channel.SendConfirmAsync($"✅ Successfully created role **{r.Name}**.").ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("⚠️ Unspecified error.").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
var r = await Context.Guild.CreateRoleAsync(roleName).ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("cr", Format.Bold(r.Name)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[RequireUserPermission(GuildPermission.ManageRoles)]
|
||||
[RequireBotPermission(GuildPermission.ManageRoles)]
|
||||
public async Task RoleColor(params string[] args)
|
||||
{
|
||||
if (args.Count() != 2 && args.Count() != 4)
|
||||
if (args.Length != 2 && args.Length != 4)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("❌ The parameters specified are **invalid.**").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("rc_params").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
var roleName = args[0].ToUpperInvariant();
|
||||
var role = Context.Guild.Roles.Where(r => r.Name.ToUpperInvariant() == roleName).FirstOrDefault();
|
||||
var role = Context.Guild.Roles.FirstOrDefault(r => r.Name.ToUpperInvariant() == roleName);
|
||||
|
||||
if (role == null)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("🚫 That role **does not exist.**").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("rc_not_exist").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
var rgb = args.Count() == 4;
|
||||
var rgb = args.Length == 4;
|
||||
var arg1 = args[1].Replace("#", "");
|
||||
|
||||
var red = Convert.ToByte(rgb ? int.Parse(arg1) : Convert.ToInt32(arg1.Substring(0, 2), 16));
|
||||
@ -219,64 +217,55 @@ namespace NadekoBot.Modules.Administration
|
||||
var blue = Convert.ToByte(rgb ? int.Parse(args[3]) : Convert.ToInt32(arg1.Substring(4, 2), 16));
|
||||
|
||||
await role.ModifyAsync(r => r.Color = new Color(red, green, blue)).ConfigureAwait(false);
|
||||
await Context.Channel.SendConfirmAsync($"☑️ Role **{role.Name}'s** color has been changed.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("rc", Format.Bold(role.Name)).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("⚠️ Error occured, most likely **invalid parameters** or **insufficient permissions.**").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("rc_perms").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[RequireUserPermission(GuildPermission.BanMembers)]
|
||||
[RequireBotPermission(GuildPermission.BanMembers)]
|
||||
public async Task Ban(IGuildUser user, [Remainder] string msg = null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(msg))
|
||||
{
|
||||
msg = "❗️No reason provided.";
|
||||
}
|
||||
if (Context.User.Id != user.Guild.OwnerId && (user.GetRoles().Select(r => r.Position).Max() >= ((IGuildUser)Context.User).GetRoles().Select(r => r.Position).Max()))
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("⚠️ You can't use this command on users with a role higher or equal to yours in the role hierarchy.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("hierarchy").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(msg))
|
||||
{
|
||||
try
|
||||
{
|
||||
await (await user.CreateDMChannelAsync()).SendErrorAsync($"⛔️ **You have been BANNED from `{Context.Guild.Name}` server.**\n" +
|
||||
$"⚖ *Reason:* {msg}").ConfigureAwait(false);
|
||||
await Task.Delay(2000).ConfigureAwait(false);
|
||||
|
||||
await user.SendErrorAsync(GetText("bandm", Format.Bold(Context.Guild.Name), msg));
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
try
|
||||
{
|
||||
await Context.Guild.AddBanAsync(user, 7).ConfigureAwait(false);
|
||||
|
||||
await Context.Channel.SendConfirmAsync("⛔️ **Banned** user **" + user.Username + "** ID: `" + user.Id + "`").ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("⚠️ **Error.** Most likely I don't have sufficient permissions.").ConfigureAwait(false);
|
||||
}
|
||||
await Context.Guild.AddBanAsync(user, 7).ConfigureAwait(false);
|
||||
await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||
.WithTitle("⛔️ " + GetText("banned_user"))
|
||||
.AddField(efb => efb.WithName(GetText("username")).WithValue(user.ToString()).WithIsInline(true))
|
||||
.AddField(efb => efb.WithName("ID").WithValue(user.Id.ToString()).WithIsInline(true)))
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[RequireUserPermission(GuildPermission.KickMembers)]
|
||||
[RequireUserPermission(GuildPermission.ManageMessages)]
|
||||
[RequireBotPermission(GuildPermission.BanMembers)]
|
||||
public async Task Softban(IGuildUser user, [Remainder] string msg = null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(msg))
|
||||
{
|
||||
msg = "❗️No reason provided.";
|
||||
}
|
||||
if (Context.User.Id != user.Guild.OwnerId && user.GetRoles().Select(r => r.Position).Max() >= ((IGuildUser)Context.User).GetRoles().Select(r => r.Position).Max())
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("⚠️ You can't use this command on users with a role higher or equal to yours in the role hierarchy.");
|
||||
await ReplyErrorLocalized("hierarchy").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -284,161 +273,160 @@ namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
try
|
||||
{
|
||||
await user.SendErrorAsync($"☣ **You have been SOFT-BANNED from `{Context.Guild.Name}` server.**\n" +
|
||||
$"⚖ *Reason:* {msg}").ConfigureAwait(false);
|
||||
await Task.Delay(2000).ConfigureAwait(false);
|
||||
await user.SendErrorAsync(GetText("sbdm", Format.Bold(Context.Guild.Name), msg));
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await Context.Guild.AddBanAsync(user, 7).ConfigureAwait(false);
|
||||
try { await Context.Guild.RemoveBanAsync(user).ConfigureAwait(false); }
|
||||
catch { await Context.Guild.RemoveBanAsync(user).ConfigureAwait(false); }
|
||||
|
||||
await Context.Channel.SendConfirmAsync("☣ **Soft-Banned** user **" + user.Username + "** ID: `" + user.Id + "`").ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("⚠️ Error. Most likely I don't have sufficient permissions.").ConfigureAwait(false);
|
||||
}
|
||||
await Context.Guild.AddBanAsync(user, 7).ConfigureAwait(false);
|
||||
try { await Context.Guild.RemoveBanAsync(user).ConfigureAwait(false); }
|
||||
catch { await Context.Guild.RemoveBanAsync(user).ConfigureAwait(false); }
|
||||
|
||||
await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||
.WithTitle("☣ " + GetText("sb_user"))
|
||||
.AddField(efb => efb.WithName(GetText("username")).WithValue(user.ToString()).WithIsInline(true))
|
||||
.AddField(efb => efb.WithName("ID").WithValue(user.Id.ToString()).WithIsInline(true)))
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[RequireUserPermission(GuildPermission.KickMembers)]
|
||||
[RequireBotPermission(GuildPermission.KickMembers)]
|
||||
public async Task Kick(IGuildUser user, [Remainder] string msg = null)
|
||||
{
|
||||
if (user == null)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("❗️User not found.").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Context.Message.Author.Id != user.Guild.OwnerId && user.GetRoles().Select(r => r.Position).Max() >= ((IGuildUser)Context.User).GetRoles().Select(r => r.Position).Max())
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("⚠️ You can't use this command on users with a role higher or equal to yours in the role hierarchy.");
|
||||
await ReplyErrorLocalized("hierarchy").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(msg))
|
||||
{
|
||||
try
|
||||
{
|
||||
await user.SendErrorAsync($"‼️**You have been KICKED from `{Context.Guild.Name}` server.**\n" +
|
||||
$"⚖ *Reason:* {msg}").ConfigureAwait(false);
|
||||
await Task.Delay(2000).ConfigureAwait(false);
|
||||
await user.SendErrorAsync(GetText("kickdm", Format.Bold(Context.Guild.Name), msg));
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
try
|
||||
{
|
||||
await user.KickAsync().ConfigureAwait(false);
|
||||
await Context.Channel.SendConfirmAsync("‼️**Kicked** user **" + user.Username + "** ID: `" + user.Id + "`").ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("⚠️ Error. Most likely I don't have sufficient permissions.").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await user.KickAsync().ConfigureAwait(false);
|
||||
await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||
.WithTitle(GetText("kicked_user"))
|
||||
.AddField(efb => efb.WithName(GetText("username")).WithValue(user.ToString()).WithIsInline(true))
|
||||
.AddField(efb => efb.WithName("ID").WithValue(user.Id.ToString()).WithIsInline(true)))
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[RequireUserPermission(GuildPermission.DeafenMembers)]
|
||||
[RequireBotPermission(GuildPermission.DeafenMembers)]
|
||||
public async Task Deafen(params IGuildUser[] users)
|
||||
{
|
||||
if (!users.Any())
|
||||
return;
|
||||
try
|
||||
foreach (var u in users)
|
||||
{
|
||||
foreach (var u in users)
|
||||
try
|
||||
{
|
||||
await u.ModifyAsync(usr => usr.Deaf = true).ConfigureAwait(false);
|
||||
}
|
||||
await Context.Channel.SendConfirmAsync("🔇 **Deafen** successful.").ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("⚠️ I most likely don't have the permission necessary for that.").ConfigureAwait(false);
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
await ReplyConfirmLocalized("deafen").ConfigureAwait(false);
|
||||
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[RequireUserPermission(GuildPermission.DeafenMembers)]
|
||||
[RequireBotPermission(GuildPermission.DeafenMembers)]
|
||||
public async Task UnDeafen(params IGuildUser[] users)
|
||||
{
|
||||
if (!users.Any())
|
||||
return;
|
||||
try
|
||||
|
||||
foreach (var u in users)
|
||||
{
|
||||
foreach (var u in users)
|
||||
try
|
||||
{
|
||||
await u.ModifyAsync(usr => usr.Deaf = false).ConfigureAwait(false);
|
||||
}
|
||||
await Context.Channel.SendConfirmAsync("🔊 **Undeafen** successful.").ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("⚠️ I most likely don't have the permission necessary for that.").ConfigureAwait(false);
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
await ReplyConfirmLocalized("undeafen").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[RequireUserPermission(GuildPermission.ManageChannels)]
|
||||
[RequireBotPermission(GuildPermission.ManageChannels)]
|
||||
public async Task DelVoiChanl([Remainder] IVoiceChannel voiceChannel)
|
||||
{
|
||||
await voiceChannel.DeleteAsync().ConfigureAwait(false);
|
||||
await Context.Channel.SendConfirmAsync($"🗑 Removed voice channel **{voiceChannel.Name}** successfully.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("delvoich", Format.Bold(voiceChannel.Name)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[RequireUserPermission(GuildPermission.ManageChannels)]
|
||||
[RequireBotPermission(GuildPermission.ManageChannels)]
|
||||
public async Task CreatVoiChanl([Remainder] string channelName)
|
||||
{
|
||||
var ch = await Context.Guild.CreateVoiceChannelAsync(channelName).ConfigureAwait(false);
|
||||
await Context.Channel.SendConfirmAsync($"✅ Created voice channel **{ch.Name}**. ID: `{ch.Id}`").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("createvoich",Format.Bold(ch.Name)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[RequireUserPermission(GuildPermission.ManageChannels)]
|
||||
[RequireBotPermission(GuildPermission.ManageChannels)]
|
||||
public async Task DelTxtChanl([Remainder] ITextChannel toDelete)
|
||||
{
|
||||
await toDelete.DeleteAsync().ConfigureAwait(false);
|
||||
await Context.Channel.SendConfirmAsync($"🗑 Removed text channel **{toDelete.Name}**. ID: `{toDelete.Id}`").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("deltextchan", Format.Bold(toDelete.Name)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[RequireUserPermission(GuildPermission.ManageChannels)]
|
||||
[RequireBotPermission(GuildPermission.ManageChannels)]
|
||||
public async Task CreaTxtChanl([Remainder] string channelName)
|
||||
{
|
||||
var txtCh = await Context.Guild.CreateTextChannelAsync(channelName).ConfigureAwait(false);
|
||||
await Context.Channel.SendConfirmAsync($"✅ Added text channel **{txtCh.Name}**. ID: `{txtCh.Id}`").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("createtextchan", Format.Bold(txtCh.Name)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[RequireUserPermission(GuildPermission.ManageChannels)]
|
||||
[RequireBotPermission(GuildPermission.ManageChannels)]
|
||||
public async Task SetTopic([Remainder] string topic = null)
|
||||
{
|
||||
var channel = (ITextChannel)Context.Channel;
|
||||
topic = topic ?? "";
|
||||
await channel.ModifyAsync(c => c.Topic = topic);
|
||||
await channel.SendConfirmAsync("🆗 **New channel topic set.**").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("set_topic").ConfigureAwait(false);
|
||||
|
||||
}
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[RequireUserPermission(GuildPermission.ManageChannels)]
|
||||
[RequireBotPermission(GuildPermission.ManageChannels)]
|
||||
public async Task SetChanlName([Remainder] string name)
|
||||
{
|
||||
var channel = (ITextChannel)Context.Channel;
|
||||
await channel.ModifyAsync(c => c.Name = name).ConfigureAwait(false);
|
||||
await channel.SendConfirmAsync("🆗 **New channel name set.**").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("set_channel_name").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
|
||||
@ -452,17 +440,18 @@ namespace NadekoBot.Modules.Administration
|
||||
var enumerable = (await Context.Channel.GetMessagesAsync().Flatten()).AsEnumerable();
|
||||
enumerable = enumerable.Where(x => x.Author.Id == user.Id);
|
||||
await Context.Channel.DeleteMessagesAsync(enumerable).ConfigureAwait(false);
|
||||
Context.Message.DeleteAfter(3);
|
||||
}
|
||||
|
||||
// prune x
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[RequireUserPermission(ChannelPermission.ManageMessages)]
|
||||
[RequireBotPermission(GuildPermission.ManageMessages)]
|
||||
public async Task Prune(int count)
|
||||
{
|
||||
if (count < 1)
|
||||
return;
|
||||
count += 1;
|
||||
await Context.Message.DeleteAsync().ConfigureAwait(false);
|
||||
int limit = (count < 100) ? count : 100;
|
||||
var enumerable = (await Context.Channel.GetMessagesAsync(limit: limit).Flatten().ConfigureAwait(false));
|
||||
@ -473,6 +462,7 @@ namespace NadekoBot.Modules.Administration
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[RequireUserPermission(ChannelPermission.ManageMessages)]
|
||||
[RequireBotPermission(GuildPermission.ManageMessages)]
|
||||
public async Task Prune(IGuildUser user, int count = 100)
|
||||
{
|
||||
if (count < 1)
|
||||
@ -484,6 +474,8 @@ namespace NadekoBot.Modules.Administration
|
||||
int limit = (count < 100) ? count : 100;
|
||||
var enumerable = (await Context.Channel.GetMessagesAsync(limit: limit).Flatten()).Where(m => m.Author == user);
|
||||
await Context.Channel.DeleteMessagesAsync(enumerable).ConfigureAwait(false);
|
||||
|
||||
Context.Message.DeleteAfter(3);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -491,11 +483,11 @@ namespace NadekoBot.Modules.Administration
|
||||
[RequireUserPermission(GuildPermission.MentionEveryone)]
|
||||
public async Task MentionRole(params IRole[] roles)
|
||||
{
|
||||
string send = $"❕{Context.User.Mention} has invoked a mention on the following roles ❕";
|
||||
string send = "❕" +GetText("menrole",Context.User.Mention);
|
||||
foreach (var role in roles)
|
||||
{
|
||||
send += $"\n**{role.Name}**\n";
|
||||
send += string.Join(", ", (await Context.Guild.GetUsersAsync()).Where(u => u.GetRoles().Contains(role)).Distinct().Select(u => u.Mention));
|
||||
send += string.Join(", ", (await Context.Guild.GetUsersAsync()).Where(u => u.GetRoles().Contains(role)).Take(50).Select(u => u.Mention));
|
||||
}
|
||||
|
||||
while (send.Length > 2000)
|
||||
@ -509,7 +501,7 @@ namespace NadekoBot.Modules.Administration
|
||||
await Context.Channel.SendMessageAsync(send).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
IGuild nadekoSupportServer;
|
||||
private IGuild _nadekoSupportServer;
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task Donators()
|
||||
{
|
||||
@ -519,18 +511,15 @@ namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
donatorsOrdered = uow.Donators.GetDonatorsOrdered();
|
||||
}
|
||||
await Context.Channel.SendConfirmAsync("Thanks to the people listed below for making this project happen!", string.Join("⭐", donatorsOrdered.Select(d => d.Name))).ConfigureAwait(false);
|
||||
await Context.Channel.SendConfirmAsync(GetText("donators"), string.Join("⭐", donatorsOrdered.Select(d => d.Name))).ConfigureAwait(false);
|
||||
|
||||
nadekoSupportServer = nadekoSupportServer ?? NadekoBot.Client.GetGuild(117523346618318850);
|
||||
_nadekoSupportServer = _nadekoSupportServer ?? NadekoBot.Client.GetGuild(117523346618318850);
|
||||
|
||||
if (nadekoSupportServer == null)
|
||||
return;
|
||||
|
||||
var patreonRole = nadekoSupportServer.GetRole(236667642088259585);
|
||||
var patreonRole = _nadekoSupportServer?.GetRole(236667642088259585);
|
||||
if (patreonRole == null)
|
||||
return;
|
||||
|
||||
var usrs = (await nadekoSupportServer.GetUsersAsync()).Where(u => u.RoleIds.Contains(236667642088259585u));
|
||||
var usrs = (await _nadekoSupportServer.GetUsersAsync()).Where(u => u.RoleIds.Contains(236667642088259585u));
|
||||
await Context.Channel.SendConfirmAsync("Patreon supporters", string.Join("⭐", usrs.Select(d => d.Username))).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@ -545,8 +534,47 @@ namespace NadekoBot.Modules.Administration
|
||||
don = uow.Donators.AddOrUpdateDonator(donator.Id, donator.Username, amount);
|
||||
await uow.CompleteAsync();
|
||||
}
|
||||
|
||||
await Context.Channel.SendConfirmAsync($"Successfuly added a new donator. Total donated amount from this user: {don.Amount} 👑").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("donadd", don.Amount).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
//[NadekoCommand, Usage, Description, Aliases]
|
||||
//[RequireContext(ContextType.Guild)]
|
||||
//public async Task Timezones(int page = 1)
|
||||
//{
|
||||
// page -= 1;
|
||||
|
||||
// if (page < 0 || page > 20)
|
||||
// return;
|
||||
|
||||
// var timezones = TimeZoneInfo.GetSystemTimeZones();
|
||||
// var timezonesPerPage = 20;
|
||||
|
||||
// await Context.Channel.SendPaginatedConfirmAsync(page + 1, (curPage) => new EmbedBuilder()
|
||||
// .WithOkColor()
|
||||
// .WithTitle("Available Timezones")
|
||||
// .WithDescription(string.Join("\n", timezones.Skip((curPage - 1) * timezonesPerPage).Take(timezonesPerPage).Select(x => $"`{x.Id,-25}` UTC{x.BaseUtcOffset:hhmm}"))),
|
||||
// timezones.Count / timezonesPerPage);
|
||||
//}
|
||||
|
||||
//[NadekoCommand, Usage, Description, Aliases]
|
||||
//[RequireContext(ContextType.Guild)]
|
||||
//public async Task Timezone([Remainder] string id)
|
||||
//{
|
||||
// TimeZoneInfo tz;
|
||||
// try
|
||||
// {
|
||||
// tz = TimeZoneInfo.FindSystemTimeZoneById(id);
|
||||
// if (tz != null)
|
||||
// await Context.Channel.SendConfirmAsync(tz.ToString()).ConfigureAwait(false);
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
// tz = null;
|
||||
// _log.Warn(ex);
|
||||
// }
|
||||
|
||||
// if (tz == null)
|
||||
// await Context.Channel.SendErrorAsync("Timezone not found. You should specify one of the timezones listed in the 'timezones' command.").ConfigureAwait(false);
|
||||
//}
|
||||
}
|
||||
}
|
@ -1,13 +1,10 @@
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using NadekoBot.Attributes;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Services;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@ -16,24 +13,23 @@ namespace NadekoBot.Modules.Administration
|
||||
public partial class Administration
|
||||
{
|
||||
[Group]
|
||||
public class AutoAssignRoleCommands : ModuleBase
|
||||
public class AutoAssignRoleCommands : NadekoSubmodule
|
||||
{
|
||||
private static Logger _log { get; }
|
||||
//guildid/roleid
|
||||
private static ConcurrentDictionary<ulong, ulong> AutoAssignedRoles { get; }
|
||||
private static ConcurrentDictionary<ulong, ulong> autoAssignedRoles { get; }
|
||||
|
||||
static AutoAssignRoleCommands()
|
||||
{
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
var log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
AutoAssignedRoles = new ConcurrentDictionary<ulong, ulong>(NadekoBot.AllGuildConfigs.Where(x => x.AutoAssignRoleId != 0)
|
||||
autoAssignedRoles = new ConcurrentDictionary<ulong, ulong>(NadekoBot.AllGuildConfigs.Where(x => x.AutoAssignRoleId != 0)
|
||||
.ToDictionary(k => k.GuildId, v => v.AutoAssignRoleId));
|
||||
NadekoBot.Client.UserJoined += async (user) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
ulong roleId = 0;
|
||||
AutoAssignedRoles.TryGetValue(user.Guild.Id, out roleId);
|
||||
ulong roleId;
|
||||
autoAssignedRoles.TryGetValue(user.Guild.Id, out roleId);
|
||||
|
||||
if (roleId == 0)
|
||||
return;
|
||||
@ -43,7 +39,7 @@ namespace NadekoBot.Modules.Administration
|
||||
if (role != null)
|
||||
await user.AddRolesAsync(role).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex) { _log.Warn(ex); }
|
||||
catch (Exception ex) { log.Warn(ex); }
|
||||
};
|
||||
}
|
||||
|
||||
@ -52,20 +48,19 @@ namespace NadekoBot.Modules.Administration
|
||||
[RequireUserPermission(GuildPermission.ManageRoles)]
|
||||
public async Task AutoAssignRole([Remainder] IRole role = null)
|
||||
{
|
||||
GuildConfig conf;
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
{
|
||||
conf = uow.GuildConfigs.For(Context.Guild.Id, set => set);
|
||||
var conf = uow.GuildConfigs.For(Context.Guild.Id, set => set);
|
||||
if (role == null)
|
||||
{
|
||||
conf.AutoAssignRoleId = 0;
|
||||
ulong throwaway;
|
||||
AutoAssignedRoles.TryRemove(Context.Guild.Id, out throwaway);
|
||||
autoAssignedRoles.TryRemove(Context.Guild.Id, out throwaway);
|
||||
}
|
||||
else
|
||||
{
|
||||
conf.AutoAssignRoleId = role.Id;
|
||||
AutoAssignedRoles.AddOrUpdate(Context.Guild.Id, role.Id, (key, val) => role.Id);
|
||||
autoAssignedRoles.AddOrUpdate(Context.Guild.Id, role.Id, (key, val) => role.Id);
|
||||
}
|
||||
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
@ -73,11 +68,11 @@ namespace NadekoBot.Modules.Administration
|
||||
|
||||
if (role == null)
|
||||
{
|
||||
await Context.Channel.SendConfirmAsync("🆗 **Auto assign role** on user join is now **disabled**.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("aar_disabled").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
await Context.Channel.SendConfirmAsync("✅ **Auto assign role** on user join is now **enabled**.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("aar_enabled").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,90 +0,0 @@
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using Discord.WebSocket;
|
||||
using NadekoBot.Attributes;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Services;
|
||||
using NLog;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
public partial class Administration
|
||||
{
|
||||
[Group]
|
||||
public class DMForwardCommands : ModuleBase
|
||||
{
|
||||
private static bool ForwardDMs { get; set; }
|
||||
private static bool ForwardDMsToAllOwners { get; set; }
|
||||
|
||||
private static readonly Logger _log;
|
||||
|
||||
static DMForwardCommands()
|
||||
{
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
{
|
||||
var config = uow.BotConfig.GetOrCreate();
|
||||
ForwardDMs = config.ForwardMessages;
|
||||
ForwardDMsToAllOwners = config.ForwardToAllOwners;
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[OwnerOnly]
|
||||
public async Task ForwardMessages()
|
||||
{
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
{
|
||||
var config = uow.BotConfig.GetOrCreate();
|
||||
ForwardDMs = config.ForwardMessages = !config.ForwardMessages;
|
||||
uow.Complete();
|
||||
}
|
||||
if (ForwardDMs)
|
||||
await Context.Channel.SendConfirmAsync("✅ **I will forward DMs from now on.**").ConfigureAwait(false);
|
||||
else
|
||||
await Context.Channel.SendConfirmAsync("🆗 **I will stop forwarding DMs from now on.**").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[OwnerOnly]
|
||||
public async Task ForwardToAll()
|
||||
{
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
{
|
||||
var config = uow.BotConfig.GetOrCreate();
|
||||
ForwardDMsToAllOwners = config.ForwardToAllOwners = !config.ForwardToAllOwners;
|
||||
uow.Complete();
|
||||
}
|
||||
if (ForwardDMsToAllOwners)
|
||||
await Context.Channel.SendConfirmAsync("ℹ️ **I will forward DMs to all owners.**").ConfigureAwait(false);
|
||||
else
|
||||
await Context.Channel.SendConfirmAsync("ℹ️ **I will forward DMs only to the first owner.**").ConfigureAwait(false);
|
||||
|
||||
}
|
||||
|
||||
public static async Task HandleDMForwarding(SocketMessage msg, List<IDMChannel> ownerChannels)
|
||||
{
|
||||
if (ForwardDMs && ownerChannels.Any())
|
||||
{
|
||||
var title = $"DM from [{msg.Author}]({msg.Author.Id})";
|
||||
if (ForwardDMsToAllOwners)
|
||||
{
|
||||
var msgs = await Task.WhenAll(ownerChannels.Where(ch => ch.Recipient.Id != msg.Author.Id)
|
||||
.Select(ch => ch.SendConfirmAsync(title, msg.Content))).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
var firstOwnerChannel = ownerChannels.First();
|
||||
if (firstOwnerChannel.Recipient.Id != msg.Author.Id)
|
||||
try { await firstOwnerChannel.SendConfirmAsync(title, msg.Content).ConfigureAwait(false); } catch { }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,241 @@
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using NadekoBot.Attributes;
|
||||
using NadekoBot.Extensions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
public partial class Administration
|
||||
{
|
||||
[Group]
|
||||
public class LocalizationCommands : NadekoSubmodule
|
||||
{
|
||||
private ImmutableDictionary<string, string> supportedLocales { get; } = new Dictionary<string, string>()
|
||||
{
|
||||
{"en-US", "English, United States"},
|
||||
{"fr-FR", "French, France"},
|
||||
{"ru-RU", "Russian, Russia"},
|
||||
{"de-DE", "German, Germany"},
|
||||
//{"nl-NL", "Dutch, Netherlands"},
|
||||
//{"ja-JP", "Japanese, Japan"},
|
||||
{"pt-BR", "Portuguese, Brazil"},
|
||||
//{"sr-Cyrl-RS", "Serbian, Serbia - Cyrillic"}
|
||||
}.ToImmutableDictionary();
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task LanguageSet()
|
||||
{
|
||||
var cul = NadekoBot.Localization.GetCultureInfo(Context.Guild);
|
||||
await ReplyConfirmLocalized("lang_set_show", Format.Bold(cul.ToString()), Format.Bold(cul.NativeName))
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[RequireUserPermission(GuildPermission.Administrator)]
|
||||
public async Task LanguageSet(string name)
|
||||
{
|
||||
try
|
||||
{
|
||||
CultureInfo ci;
|
||||
if (name.Trim().ToLowerInvariant() == "default")
|
||||
{
|
||||
NadekoBot.Localization.RemoveGuildCulture(Context.Guild);
|
||||
ci = NadekoBot.Localization.DefaultCultureInfo;
|
||||
}
|
||||
else
|
||||
{
|
||||
ci = new CultureInfo(name);
|
||||
NadekoBot.Localization.SetGuildCulture(Context.Guild, ci);
|
||||
}
|
||||
|
||||
await ReplyConfirmLocalized("lang_set", Format.Bold(ci.ToString()), Format.Bold(ci.NativeName)).ConfigureAwait(false);
|
||||
}
|
||||
catch(Exception)
|
||||
{
|
||||
await ReplyErrorLocalized("lang_set_fail").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task LanguageSetDefault()
|
||||
{
|
||||
var cul = NadekoBot.Localization.DefaultCultureInfo;
|
||||
await ReplyConfirmLocalized("lang_set_bot_show", cul, cul.NativeName).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[OwnerOnly]
|
||||
public async Task LanguageSetDefault(string name)
|
||||
{
|
||||
try
|
||||
{
|
||||
CultureInfo ci;
|
||||
if (name.Trim().ToLowerInvariant() == "default")
|
||||
{
|
||||
NadekoBot.Localization.ResetDefaultCulture();
|
||||
ci = NadekoBot.Localization.DefaultCultureInfo;
|
||||
}
|
||||
else
|
||||
{
|
||||
ci = new CultureInfo(name);
|
||||
NadekoBot.Localization.SetDefaultCulture(ci);
|
||||
}
|
||||
await ReplyConfirmLocalized("lang_set_bot", Format.Bold(ci.ToString()), Format.Bold(ci.NativeName)).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
await ReplyErrorLocalized("lang_set_fail").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[OwnerOnly]
|
||||
public async Task LanguagesList()
|
||||
{
|
||||
await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||
.WithTitle(GetText("lang_list", ""))
|
||||
.WithDescription(string.Join("\n",
|
||||
supportedLocales.Select(x => $"{Format.Code(x.Key), -10} => {x.Value}"))));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* list of language codes for reference.
|
||||
* taken from https://github.com/dotnet/coreclr/blob/ee5862c6a257e60e263537d975ab6c513179d47f/src/mscorlib/src/System/Globalization/CultureData.cs#L192
|
||||
{ "029", "en-029" },
|
||||
{ "AE", "ar-AE" },
|
||||
{ "AF", "prs-AF" },
|
||||
{ "AL", "sq-AL" },
|
||||
{ "AM", "hy-AM" },
|
||||
{ "AR", "es-AR" },
|
||||
{ "AT", "de-AT" },
|
||||
{ "AU", "en-AU" },
|
||||
{ "AZ", "az-Cyrl-AZ" },
|
||||
{ "BA", "bs-Latn-BA" },
|
||||
{ "BD", "bn-BD" },
|
||||
{ "BE", "nl-BE" },
|
||||
{ "BG", "bg-BG" },
|
||||
{ "BH", "ar-BH" },
|
||||
{ "BN", "ms-BN" },
|
||||
{ "BO", "es-BO" },
|
||||
{ "BR", "pt-BR" },
|
||||
{ "BY", "be-BY" },
|
||||
{ "BZ", "en-BZ" },
|
||||
{ "CA", "en-CA" },
|
||||
{ "CH", "it-CH" },
|
||||
{ "CL", "es-CL" },
|
||||
{ "CN", "zh-CN" },
|
||||
{ "CO", "es-CO" },
|
||||
{ "CR", "es-CR" },
|
||||
{ "CS", "sr-Cyrl-CS" },
|
||||
{ "CZ", "cs-CZ" },
|
||||
{ "DE", "de-DE" },
|
||||
{ "DK", "da-DK" },
|
||||
{ "DO", "es-DO" },
|
||||
{ "DZ", "ar-DZ" },
|
||||
{ "EC", "es-EC" },
|
||||
{ "EE", "et-EE" },
|
||||
{ "EG", "ar-EG" },
|
||||
{ "ES", "es-ES" },
|
||||
{ "ET", "am-ET" },
|
||||
{ "FI", "fi-FI" },
|
||||
{ "FO", "fo-FO" },
|
||||
{ "FR", "fr-FR" },
|
||||
{ "GB", "en-GB" },
|
||||
{ "GE", "ka-GE" },
|
||||
{ "GL", "kl-GL" },
|
||||
{ "GR", "el-GR" },
|
||||
{ "GT", "es-GT" },
|
||||
{ "HK", "zh-HK" },
|
||||
{ "HN", "es-HN" },
|
||||
{ "HR", "hr-HR" },
|
||||
{ "HU", "hu-HU" },
|
||||
{ "ID", "id-ID" },
|
||||
{ "IE", "en-IE" },
|
||||
{ "IL", "he-IL" },
|
||||
{ "IN", "hi-IN" },
|
||||
{ "IQ", "ar-IQ" },
|
||||
{ "IR", "fa-IR" },
|
||||
{ "IS", "is-IS" },
|
||||
{ "IT", "it-IT" },
|
||||
{ "IV", "" },
|
||||
{ "JM", "en-JM" },
|
||||
{ "JO", "ar-JO" },
|
||||
{ "JP", "ja-JP" },
|
||||
{ "KE", "sw-KE" },
|
||||
{ "KG", "ky-KG" },
|
||||
{ "KH", "km-KH" },
|
||||
{ "KR", "ko-KR" },
|
||||
{ "KW", "ar-KW" },
|
||||
{ "KZ", "kk-KZ" },
|
||||
{ "LA", "lo-LA" },
|
||||
{ "LB", "ar-LB" },
|
||||
{ "LI", "de-LI" },
|
||||
{ "LK", "si-LK" },
|
||||
{ "LT", "lt-LT" },
|
||||
{ "LU", "lb-LU" },
|
||||
{ "LV", "lv-LV" },
|
||||
{ "LY", "ar-LY" },
|
||||
{ "MA", "ar-MA" },
|
||||
{ "MC", "fr-MC" },
|
||||
{ "ME", "sr-Latn-ME" },
|
||||
{ "MK", "mk-MK" },
|
||||
{ "MN", "mn-MN" },
|
||||
{ "MO", "zh-MO" },
|
||||
{ "MT", "mt-MT" },
|
||||
{ "MV", "dv-MV" },
|
||||
{ "MX", "es-MX" },
|
||||
{ "MY", "ms-MY" },
|
||||
{ "NG", "ig-NG" },
|
||||
{ "NI", "es-NI" },
|
||||
{ "NL", "nl-NL" },
|
||||
{ "NO", "nn-NO" },
|
||||
{ "NP", "ne-NP" },
|
||||
{ "NZ", "en-NZ" },
|
||||
{ "OM", "ar-OM" },
|
||||
{ "PA", "es-PA" },
|
||||
{ "PE", "es-PE" },
|
||||
{ "PH", "en-PH" },
|
||||
{ "PK", "ur-PK" },
|
||||
{ "PL", "pl-PL" },
|
||||
{ "PR", "es-PR" },
|
||||
{ "PT", "pt-PT" },
|
||||
{ "PY", "es-PY" },
|
||||
{ "QA", "ar-QA" },
|
||||
{ "RO", "ro-RO" },
|
||||
{ "RS", "sr-Latn-RS" },
|
||||
{ "RU", "ru-RU" },
|
||||
{ "RW", "rw-RW" },
|
||||
{ "SA", "ar-SA" },
|
||||
{ "SE", "sv-SE" },
|
||||
{ "SG", "zh-SG" },
|
||||
{ "SI", "sl-SI" },
|
||||
{ "SK", "sk-SK" },
|
||||
{ "SN", "wo-SN" },
|
||||
{ "SV", "es-SV" },
|
||||
{ "SY", "ar-SY" },
|
||||
{ "TH", "th-TH" },
|
||||
{ "TJ", "tg-Cyrl-TJ" },
|
||||
{ "TM", "tk-TM" },
|
||||
{ "TN", "ar-TN" },
|
||||
{ "TR", "tr-TR" },
|
||||
{ "TT", "en-TT" },
|
||||
{ "TW", "zh-TW" },
|
||||
{ "UA", "uk-UA" },
|
||||
{ "US", "en-US" },
|
||||
{ "UY", "es-UY" },
|
||||
{ "UZ", "uz-Cyrl-UZ" },
|
||||
{ "VE", "es-VE" },
|
||||
{ "VN", "vi-VN" },
|
||||
{ "YE", "ar-YE" },
|
||||
{ "ZA", "af-ZA" },
|
||||
{ "ZW", "en-ZW" }
|
||||
*/
|
@ -1,7 +1,6 @@
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using Discord.WebSocket;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NadekoBot.Attributes;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Modules.Permissions;
|
||||
@ -21,45 +20,43 @@ namespace NadekoBot.Modules.Administration
|
||||
public partial class Administration
|
||||
{
|
||||
[Group]
|
||||
public class LogCommands : ModuleBase
|
||||
public class LogCommands : NadekoSubmodule
|
||||
{
|
||||
private const string clockEmojiUrl = "https://cdn.discordapp.com/attachments/155726317222887425/258309524966866945/clock.png";
|
||||
|
||||
private static DiscordShardedClient _client { get; }
|
||||
private static Logger _log { get; }
|
||||
private static DiscordShardedClient client { get; }
|
||||
private new static Logger _log { get; }
|
||||
|
||||
private static string prettyCurrentTime => $"【{DateTime.Now:HH:mm:ss}】";
|
||||
private static string currentTime => $"{DateTime.Now:HH:mm:ss}";
|
||||
|
||||
public static ConcurrentDictionary<ulong, LogSetting> GuildLogSettings { get; }
|
||||
|
||||
private static ConcurrentDictionary<ITextChannel, List<string>> PresenceUpdates { get; } = new ConcurrentDictionary<ITextChannel, List<string>>();
|
||||
private static Timer timerReference { get; }
|
||||
private IGoogleApiService _google { get; }
|
||||
private static ConcurrentDictionary<ITextChannel, List<string>> presenceUpdates { get; } = new ConcurrentDictionary<ITextChannel, List<string>>();
|
||||
private static readonly Timer _timerReference;
|
||||
|
||||
static LogCommands()
|
||||
{
|
||||
_client = NadekoBot.Client;
|
||||
client = NadekoBot.Client;
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
var sw = Stopwatch.StartNew();
|
||||
|
||||
GuildLogSettings = new ConcurrentDictionary<ulong, LogSetting>(NadekoBot.AllGuildConfigs
|
||||
.ToDictionary(g => g.GuildId, g => g.LogSetting));
|
||||
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
{
|
||||
GuildLogSettings = new ConcurrentDictionary<ulong, LogSetting>(NadekoBot.AllGuildConfigs
|
||||
.ToDictionary(g => g.GuildId, g => g.LogSetting));
|
||||
}
|
||||
|
||||
timerReference = new Timer(async (state) =>
|
||||
_timerReference = new Timer(async (state) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var keys = PresenceUpdates.Keys.ToList();
|
||||
var keys = presenceUpdates.Keys.ToList();
|
||||
|
||||
await Task.WhenAll(keys.Select(async key =>
|
||||
{
|
||||
List<string> messages;
|
||||
if (PresenceUpdates.TryRemove(key, out messages))
|
||||
try { await key.SendConfirmAsync("Presence Updates", string.Join(Environment.NewLine, messages)); } catch { }
|
||||
if (presenceUpdates.TryRemove(key, out messages))
|
||||
try { await key.SendConfirmAsync(key.Guild.GetLogText("presence_updates"), string.Join(Environment.NewLine, messages)); }
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}));
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -72,23 +69,22 @@ namespace NadekoBot.Modules.Administration
|
||||
_log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s");
|
||||
|
||||
//_client.MessageReceived += _client_MessageReceived;
|
||||
_client.MessageUpdated += _client_MessageUpdated;
|
||||
_client.MessageDeleted += _client_MessageDeleted;
|
||||
_client.UserBanned += _client_UserBanned;
|
||||
_client.UserUnbanned += _client_UserUnbanned;
|
||||
_client.UserJoined += _client_UserJoined;
|
||||
_client.UserLeft += _client_UserLeft;
|
||||
_client.UserPresenceUpdated += _client_UserPresenceUpdated;
|
||||
_client.UserVoiceStateUpdated += _client_UserVoiceStateUpdated;
|
||||
_client.UserVoiceStateUpdated += _client_UserVoiceStateUpdated_TTS;
|
||||
_client.GuildMemberUpdated += _client_GuildUserUpdated;
|
||||
client.MessageUpdated += _client_MessageUpdated;
|
||||
client.MessageDeleted += _client_MessageDeleted;
|
||||
client.UserBanned += _client_UserBanned;
|
||||
client.UserUnbanned += _client_UserUnbanned;
|
||||
client.UserJoined += _client_UserJoined;
|
||||
client.UserLeft += _client_UserLeft;
|
||||
client.UserPresenceUpdated += _client_UserPresenceUpdated;
|
||||
client.UserVoiceStateUpdated += _client_UserVoiceStateUpdated;
|
||||
client.UserVoiceStateUpdated += _client_UserVoiceStateUpdated_TTS;
|
||||
client.GuildMemberUpdated += _client_GuildUserUpdated;
|
||||
#if !GLOBAL_NADEKO
|
||||
_client.UserUpdated += _client_UserUpdated;
|
||||
client.UserUpdated += _client_UserUpdated;
|
||||
#endif
|
||||
|
||||
_client.ChannelCreated += _client_ChannelCreated;
|
||||
_client.ChannelDestroyed += _client_ChannelDestroyed;
|
||||
_client.ChannelUpdated += _client_ChannelUpdated;
|
||||
client.ChannelCreated += _client_ChannelCreated;
|
||||
client.ChannelDestroyed += _client_ChannelDestroyed;
|
||||
client.ChannelUpdated += _client_ChannelUpdated;
|
||||
|
||||
MuteCommands.UserMuted += MuteCommands_UserMuted;
|
||||
MuteCommands.UserUnmuted += MuteCommands_UserUnmuted;
|
||||
@ -119,7 +115,7 @@ namespace NadekoBot.Modules.Administration
|
||||
|
||||
if (before.Username != after.Username)
|
||||
{
|
||||
embed.WithTitle("👥 Username Changed")
|
||||
embed.WithTitle("👥 " + g.GetLogText("username_changed"))
|
||||
.WithDescription($"{before.Username}#{before.Discriminator} | {before.Id}")
|
||||
.AddField(fb => fb.WithName("Old Name").WithValue($"{before.Username}").WithIsInline(true))
|
||||
.AddField(fb => fb.WithName("New Name").WithValue($"{after.Username}").WithIsInline(true))
|
||||
@ -128,9 +124,8 @@ namespace NadekoBot.Modules.Administration
|
||||
}
|
||||
else if (before.AvatarUrl != after.AvatarUrl)
|
||||
{
|
||||
embed.WithTitle("👥 Avatar Changed")
|
||||
embed.WithTitle("👥" + g.GetLogText("avatar_changed"))
|
||||
.WithDescription($"{before.Username}#{before.Discriminator} | {before.Id}")
|
||||
.WithTitle($"{before.Username}#{before.Discriminator} | {before.Id}")
|
||||
.WithThumbnailUrl(before.AvatarUrl)
|
||||
.WithImageUrl(after.AvatarUrl)
|
||||
.WithFooter(fb => fb.WithText(currentTime))
|
||||
@ -159,7 +154,9 @@ namespace NadekoBot.Modules.Administration
|
||||
//}
|
||||
}
|
||||
catch
|
||||
{ }
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task _client_UserVoiceStateUpdated_TTS(SocketUser iusr, SocketVoiceState before, SocketVoiceState after)
|
||||
@ -188,20 +185,23 @@ namespace NadekoBot.Modules.Administration
|
||||
var str = "";
|
||||
if (beforeVch?.Guild == afterVch?.Guild)
|
||||
{
|
||||
str = $"{usr.Username} moved from {beforeVch.Name} to {afterVch.Name}";
|
||||
str = logChannel.Guild.GetLogText("moved", usr.Username, beforeVch?.Name, afterVch?.Name);
|
||||
}
|
||||
else if (beforeVch == null)
|
||||
{
|
||||
str = $"{usr.Username} has joined {afterVch.Name}";
|
||||
str = logChannel.Guild.GetLogText("joined", usr.Username, afterVch.Name);
|
||||
}
|
||||
else if (afterVch == null)
|
||||
{
|
||||
str = $"{usr.Username} has left {beforeVch.Name}";
|
||||
str = logChannel.Guild.GetLogText("left", usr.Username, beforeVch.Name);
|
||||
}
|
||||
var toDelete = await logChannel.SendMessageAsync(str, true).ConfigureAwait(false);
|
||||
toDelete.DeleteAfter(5);
|
||||
}
|
||||
catch { }
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
private static async void MuteCommands_UserMuted(IGuildUser usr, MuteCommands.MuteType muteType)
|
||||
@ -216,28 +216,32 @@ namespace NadekoBot.Modules.Administration
|
||||
ITextChannel logChannel;
|
||||
if ((logChannel = await TryGetLogChannel(usr.Guild, logSetting, LogType.UserMuted)) == null)
|
||||
return;
|
||||
string mutes = "";
|
||||
var mutes = "";
|
||||
var mutedLocalized = logChannel.Guild.GetLogText("muted_sn");
|
||||
switch (muteType)
|
||||
{
|
||||
case MuteCommands.MuteType.Voice:
|
||||
mutes = "voice chat";
|
||||
mutes = "🔇 " + logChannel.Guild.GetLogText("xmuted_voice", mutedLocalized);
|
||||
break;
|
||||
case MuteCommands.MuteType.Chat:
|
||||
mutes = "text chat";
|
||||
mutes = "🔇 " + logChannel.Guild.GetLogText("xmuted_text", mutedLocalized);
|
||||
break;
|
||||
case MuteCommands.MuteType.All:
|
||||
mutes = "text and voice chat";
|
||||
mutes = "🔇 " + logChannel.Guild.GetLogText("xmuted_text_and_voice", mutedLocalized);
|
||||
break;
|
||||
}
|
||||
|
||||
var embed = new EmbedBuilder().WithAuthor(eab => eab.WithName("🔇 User Muted from " + mutes))
|
||||
var embed = new EmbedBuilder().WithAuthor(eab => eab.WithName(mutes))
|
||||
.WithTitle($"{usr.Username}#{usr.Discriminator} | {usr.Id}")
|
||||
.WithFooter(fb => fb.WithText(currentTime))
|
||||
.WithOkColor();
|
||||
|
||||
await logChannel.EmbedAsync(embed).ConfigureAwait(false);
|
||||
}
|
||||
catch { }
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
private static async void MuteCommands_UserUnmuted(IGuildUser usr, MuteCommands.MuteType muteType)
|
||||
@ -253,28 +257,32 @@ namespace NadekoBot.Modules.Administration
|
||||
if ((logChannel = await TryGetLogChannel(usr.Guild, logSetting, LogType.UserMuted)) == null)
|
||||
return;
|
||||
|
||||
string mutes = "";
|
||||
var mutes = "";
|
||||
var unmutedLocalized = logChannel.Guild.GetLogText("unmuted_sn");
|
||||
switch (muteType)
|
||||
{
|
||||
case MuteCommands.MuteType.Voice:
|
||||
mutes = "voice chat";
|
||||
mutes = "🔊 " + logChannel.Guild.GetLogText("xmuted_voice", unmutedLocalized);
|
||||
break;
|
||||
case MuteCommands.MuteType.Chat:
|
||||
mutes = "text chat";
|
||||
mutes = "🔊 " + logChannel.Guild.GetLogText("xmuted_text", unmutedLocalized);
|
||||
break;
|
||||
case MuteCommands.MuteType.All:
|
||||
mutes = "text and voice chat";
|
||||
mutes = "🔊 " + logChannel.Guild.GetLogText("xmuted_text_and_voice", unmutedLocalized);
|
||||
break;
|
||||
}
|
||||
|
||||
var embed = new EmbedBuilder().WithAuthor(eab => eab.WithName("🔊 User Unmuted from " + mutes))
|
||||
var embed = new EmbedBuilder().WithAuthor(eab => eab.WithName(mutes))
|
||||
.WithTitle($"{usr.Username}#{usr.Discriminator} | {usr.Id}")
|
||||
.WithFooter(fb => fb.WithText($"{currentTime}"))
|
||||
.WithOkColor();
|
||||
|
||||
await logChannel.EmbedAsync(embed).ConfigureAwait(false);
|
||||
}
|
||||
catch { }
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task TriggeredAntiProtection(IGuildUser[] users, PunishmentAction action, ProtectionType protection)
|
||||
@ -293,28 +301,31 @@ namespace NadekoBot.Modules.Administration
|
||||
return;
|
||||
|
||||
var punishment = "";
|
||||
if (action == PunishmentAction.Mute)
|
||||
switch (action)
|
||||
{
|
||||
punishment = "🔇 MUTED";
|
||||
}
|
||||
else if (action == PunishmentAction.Kick)
|
||||
{
|
||||
punishment = "☣ SOFT-BANNED (KICKED)";
|
||||
}
|
||||
else if (action == PunishmentAction.Ban)
|
||||
{
|
||||
punishment = "⛔️ BANNED";
|
||||
case PunishmentAction.Mute:
|
||||
punishment = "🔇 " + logChannel.Guild.GetLogText("muted_pl").ToUpperInvariant();
|
||||
break;
|
||||
case PunishmentAction.Kick:
|
||||
punishment = "☣ " + logChannel.Guild.GetLogText("soft_banned_pl").ToUpperInvariant();
|
||||
break;
|
||||
case PunishmentAction.Ban:
|
||||
punishment = "⛔️ " + logChannel.Guild.GetLogText("banned_pl").ToUpperInvariant();
|
||||
break;
|
||||
}
|
||||
|
||||
var embed = new EmbedBuilder().WithAuthor(eab => eab.WithName($"🛡 Anti-{protection}"))
|
||||
.WithTitle($"Users " + punishment)
|
||||
.WithDescription(String.Join("\n", users.Select(u => u.ToString())))
|
||||
.WithTitle(logChannel.Guild.GetLogText("users") + " " + punishment)
|
||||
.WithDescription(string.Join("\n", users.Select(u => u.ToString())))
|
||||
.WithFooter(fb => fb.WithText($"{currentTime}"))
|
||||
.WithOkColor();
|
||||
|
||||
await logChannel.EmbedAsync(embed).ConfigureAwait(false);
|
||||
}
|
||||
catch { }
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task _client_GuildUserUpdated(SocketGuildUser before, SocketGuildUser after)
|
||||
@ -333,23 +344,23 @@ namespace NadekoBot.Modules.Administration
|
||||
.WithTitle($"{before.Username}#{before.Discriminator} | {before.Id}");
|
||||
if (before.Nickname != after.Nickname)
|
||||
{
|
||||
embed.WithAuthor(eab => eab.WithName("👥 Nickname Changed"))
|
||||
embed.WithAuthor(eab => eab.WithName("👥 " + logChannel.Guild.GetLogText("nick_change")))
|
||||
|
||||
.AddField(efb => efb.WithName("Old Nickname").WithValue($"{before.Nickname}#{before.Discriminator}"))
|
||||
.AddField(efb => efb.WithName("New Nickname").WithValue($"{after.Nickname}#{after.Discriminator}"));
|
||||
.AddField(efb => efb.WithName(logChannel.Guild.GetLogText("old_nick")).WithValue($"{before.Nickname}#{before.Discriminator}"))
|
||||
.AddField(efb => efb.WithName(logChannel.Guild.GetLogText("new_nick")).WithValue($"{after.Nickname}#{after.Discriminator}"));
|
||||
}
|
||||
else if (!before.RoleIds.SequenceEqual(after.RoleIds))
|
||||
{
|
||||
if (before.RoleIds.Count < after.RoleIds.Count)
|
||||
{
|
||||
var diffRoles = after.RoleIds.Where(r => !before.RoleIds.Contains(r)).Select(r => before.Guild.GetRole(r).Name);
|
||||
embed.WithAuthor(eab => eab.WithName("⚔ User's Role Added"))
|
||||
embed.WithAuthor(eab => eab.WithName("⚔ " + logChannel.Guild.GetLogText("user_role_add")))
|
||||
.WithDescription(string.Join(", ", diffRoles).SanitizeMentions());
|
||||
}
|
||||
else if (before.RoleIds.Count > after.RoleIds.Count)
|
||||
{
|
||||
var diffRoles = before.RoleIds.Where(r => !after.RoleIds.Contains(r)).Select(r => before.Guild.GetRole(r).Name);
|
||||
embed.WithAuthor(eab => eab.WithName("⚔ User's Role Removed"))
|
||||
embed.WithAuthor(eab => eab.WithName("⚔ " + logChannel.Guild.GetLogText("user_role_rem")))
|
||||
.WithDescription(string.Join(", ", diffRoles).SanitizeMentions());
|
||||
}
|
||||
}
|
||||
@ -357,7 +368,10 @@ namespace NadekoBot.Modules.Administration
|
||||
return;
|
||||
await logChannel.EmbedAsync(embed).ConfigureAwait(false);
|
||||
}
|
||||
catch { }
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task _client_ChannelUpdated(IChannel cbefore, IChannel cafter)
|
||||
@ -386,23 +400,26 @@ namespace NadekoBot.Modules.Administration
|
||||
|
||||
if (before.Name != after.Name)
|
||||
{
|
||||
embed.WithTitle("ℹ️ Channel Name Changed")
|
||||
embed.WithTitle("ℹ️ " + logChannel.Guild.GetLogText("ch_name_change"))
|
||||
.WithDescription($"{after} | {after.Id}")
|
||||
.AddField(efb => efb.WithName("Old Name").WithValue(before.Name));
|
||||
.AddField(efb => efb.WithName(logChannel.Guild.GetLogText("ch_old_name")).WithValue(before.Name));
|
||||
}
|
||||
else if (beforeTextChannel?.Topic != afterTextChannel?.Topic)
|
||||
{
|
||||
embed.WithTitle("ℹ️ Channel Topic Changed")
|
||||
embed.WithTitle("ℹ️ " + logChannel.Guild.GetLogText("ch_topic_change"))
|
||||
.WithDescription($"{after} | {after.Id}")
|
||||
.AddField(efb => efb.WithName("Old Topic").WithValue(beforeTextChannel.Topic))
|
||||
.AddField(efb => efb.WithName("New Topic").WithValue(afterTextChannel.Topic));
|
||||
.AddField(efb => efb.WithName(logChannel.Guild.GetLogText("old_topic")).WithValue(beforeTextChannel?.Topic ?? "-"))
|
||||
.AddField(efb => efb.WithName(logChannel.Guild.GetLogText("new_topic")).WithValue(afterTextChannel?.Topic ?? "-"));
|
||||
}
|
||||
else
|
||||
return;
|
||||
|
||||
await logChannel.EmbedAsync(embed).ConfigureAwait(false);
|
||||
}
|
||||
catch { }
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task _client_ChannelDestroyed(IChannel ich)
|
||||
@ -422,14 +439,23 @@ namespace NadekoBot.Modules.Administration
|
||||
ITextChannel logChannel;
|
||||
if ((logChannel = await TryGetLogChannel(ch.Guild, logSetting, LogType.ChannelDestroyed)) == null)
|
||||
return;
|
||||
|
||||
string title;
|
||||
if (ch is IVoiceChannel)
|
||||
{
|
||||
title = logChannel.Guild.GetLogText("voice_chan_destroyed");
|
||||
}
|
||||
else
|
||||
title = logChannel.Guild.GetLogText("text_chan_destroyed");
|
||||
await logChannel.EmbedAsync(new EmbedBuilder()
|
||||
.WithOkColor()
|
||||
.WithTitle("🆕 " + (ch is IVoiceChannel ? "Voice" : "Text") + " Channel Destroyed")
|
||||
.WithTitle("🆕 " + title)
|
||||
.WithDescription($"{ch.Name} | {ch.Id}")
|
||||
.WithFooter(efb => efb.WithText(currentTime))).ConfigureAwait(false);
|
||||
}
|
||||
catch { }
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task _client_ChannelCreated(IChannel ich)
|
||||
@ -448,10 +474,16 @@ namespace NadekoBot.Modules.Administration
|
||||
ITextChannel logChannel;
|
||||
if ((logChannel = await TryGetLogChannel(ch.Guild, logSetting, LogType.ChannelCreated)) == null)
|
||||
return;
|
||||
|
||||
string title;
|
||||
if (ch is IVoiceChannel)
|
||||
{
|
||||
title = logChannel.Guild.GetLogText("voice_chan_created");
|
||||
}
|
||||
else
|
||||
title = logChannel.Guild.GetLogText("text_chan_created");
|
||||
await logChannel.EmbedAsync(new EmbedBuilder()
|
||||
.WithOkColor()
|
||||
.WithTitle("🆕 " + (ch is IVoiceChannel ? "Voice" : "Text") + " Channel Created")
|
||||
.WithTitle("🆕 " + title)
|
||||
.WithDescription($"{ch.Name} | {ch.Id}")
|
||||
.WithFooter(efb => efb.WithText(currentTime))).ConfigureAwait(false);
|
||||
}
|
||||
@ -484,20 +516,29 @@ namespace NadekoBot.Modules.Administration
|
||||
string str = null;
|
||||
if (beforeVch?.Guild == afterVch?.Guild)
|
||||
{
|
||||
str = $"🎙`{prettyCurrentTime}`👤__**{usr.Username}#{usr.Discriminator}**__ moved from **{beforeVch.Name}** to **{afterVch.Name}** voice channel.";
|
||||
str = "🎙" + Format.Code(prettyCurrentTime) + logChannel.Guild.GetLogText("user_vmoved",
|
||||
"👤" + Format.Bold(usr.Username + "#" + usr.Discriminator),
|
||||
Format.Bold(beforeVch?.Name ?? ""), Format.Bold(afterVch?.Name ?? ""));
|
||||
}
|
||||
else if (beforeVch == null)
|
||||
{
|
||||
str = $"🎙`{prettyCurrentTime}`👤__**{usr.Username}#{usr.Discriminator}**__ has joined **{afterVch.Name}** voice channel.";
|
||||
str = "🎙" + Format.Code(prettyCurrentTime) + logChannel.Guild.GetLogText("user_vjoined",
|
||||
"👤" + Format.Bold(usr.Username + "#" + usr.Discriminator),
|
||||
Format.Bold(afterVch.Name ?? ""));
|
||||
}
|
||||
else if (afterVch == null)
|
||||
{
|
||||
str = $"🎙`{prettyCurrentTime}`👤__**{usr.Username}#{usr.Discriminator}**__ has left **{beforeVch.Name}** voice channel.";
|
||||
str = "🎙" + Format.Code(prettyCurrentTime) + logChannel.Guild.GetLogText("user_vleft",
|
||||
"👤" + Format.Bold(usr.Username + "#" + usr.Discriminator),
|
||||
Format.Bold(beforeVch.Name ?? ""));
|
||||
}
|
||||
if (str != null)
|
||||
PresenceUpdates.AddOrUpdate(logChannel, new List<string>() { str }, (id, list) => { list.Add(str); return list; });
|
||||
presenceUpdates.AddOrUpdate(logChannel, new List<string>() { str }, (id, list) => { list.Add(str); return list; });
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
private static async Task _client_UserPresenceUpdated(Optional<SocketGuild> optGuild, SocketUser usr, SocketPresence before, SocketPresence after)
|
||||
@ -520,7 +561,10 @@ namespace NadekoBot.Modules.Administration
|
||||
return;
|
||||
string str = "";
|
||||
if (before.Status != after.Status)
|
||||
str = $"🎭`{prettyCurrentTime}`👤__**{usr.Username}**__ is now **{after.Status}**.";
|
||||
str = "🎭" + Format.Code(prettyCurrentTime) +
|
||||
logChannel.Guild.GetLogText("user_status_change",
|
||||
"👤" + Format.Bold(usr.Username),
|
||||
Format.Bold(after.Status.ToString()));
|
||||
|
||||
//if (before.Game?.Name != after.Game?.Name)
|
||||
//{
|
||||
@ -529,9 +573,12 @@ namespace NadekoBot.Modules.Administration
|
||||
// str += $"👾`{prettyCurrentTime}`👤__**{usr.Username}**__ is now playing **{after.Game?.Name}**.";
|
||||
//}
|
||||
|
||||
PresenceUpdates.AddOrUpdate(logChannel, new List<string>() { str }, (id, list) => { list.Add(str); return list; });
|
||||
presenceUpdates.AddOrUpdate(logChannel, new List<string>() { str }, (id, list) => { list.Add(str); return list; });
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
private static async Task _client_UserLeft(IGuildUser usr)
|
||||
@ -549,13 +596,16 @@ namespace NadekoBot.Modules.Administration
|
||||
|
||||
await logChannel.EmbedAsync(new EmbedBuilder()
|
||||
.WithOkColor()
|
||||
.WithTitle("❌ User Left")
|
||||
.WithTitle("❌ " + logChannel.Guild.GetLogText("user_left"))
|
||||
.WithThumbnailUrl(usr.AvatarUrl)
|
||||
.WithDescription(usr.ToString())
|
||||
.AddField(efb => efb.WithName("Id").WithValue(usr.Id.ToString()))
|
||||
.WithFooter(efb => efb.WithText(currentTime))).ConfigureAwait(false);
|
||||
}
|
||||
catch { }
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task _client_UserJoined(IGuildUser usr)
|
||||
@ -573,7 +623,7 @@ namespace NadekoBot.Modules.Administration
|
||||
|
||||
await logChannel.EmbedAsync(new EmbedBuilder()
|
||||
.WithOkColor()
|
||||
.WithTitle("✅ User Joined")
|
||||
.WithTitle("✅ " + logChannel.Guild.GetLogText("user_joined"))
|
||||
.WithThumbnailUrl(usr.AvatarUrl)
|
||||
.WithDescription($"{usr}")
|
||||
.AddField(efb => efb.WithName("Id").WithValue(usr.Id.ToString()))
|
||||
@ -597,7 +647,7 @@ namespace NadekoBot.Modules.Administration
|
||||
|
||||
await logChannel.EmbedAsync(new EmbedBuilder()
|
||||
.WithOkColor()
|
||||
.WithTitle("♻️ User Unbanned")
|
||||
.WithTitle("♻️ " + logChannel.Guild.GetLogText("user_unbanned"))
|
||||
.WithThumbnailUrl(usr.AvatarUrl)
|
||||
.WithDescription(usr.ToString())
|
||||
.AddField(efb => efb.WithName("Id").WithValue(usr.Id.ToString()))
|
||||
@ -620,7 +670,7 @@ namespace NadekoBot.Modules.Administration
|
||||
return;
|
||||
await logChannel.EmbedAsync(new EmbedBuilder()
|
||||
.WithOkColor()
|
||||
.WithTitle("🚫 User Banned")
|
||||
.WithTitle("🚫 " + logChannel.Guild.GetLogText("user_banned"))
|
||||
.WithThumbnailUrl(usr.AvatarUrl)
|
||||
.WithDescription(usr.ToString())
|
||||
.AddField(efb => efb.WithName("Id").WithValue(usr.Id.ToString()))
|
||||
@ -653,17 +703,21 @@ namespace NadekoBot.Modules.Administration
|
||||
return;
|
||||
var embed = new EmbedBuilder()
|
||||
.WithOkColor()
|
||||
.WithTitle($"🗑 Message Deleted in {((ITextChannel)msg.Channel).Mention}")
|
||||
.WithDescription($"{msg.Author}")
|
||||
.AddField(efb => efb.WithName("Content").WithValue(msg.Resolve(userHandling: TagHandling.FullName)).WithIsInline(false))
|
||||
.WithTitle("🗑 " + logChannel.Guild.GetLogText("msg_del", ((ITextChannel)msg.Channel).Name))
|
||||
.WithDescription(msg.Author.ToString())
|
||||
.AddField(efb => efb.WithName(logChannel.Guild.GetLogText("content")).WithValue(string.IsNullOrWhiteSpace(msg.Content) ? "-" : msg.Resolve(userHandling: TagHandling.FullName)).WithIsInline(false))
|
||||
.AddField(efb => efb.WithName("Id").WithValue(msg.Id.ToString()).WithIsInline(false))
|
||||
.WithFooter(efb => efb.WithText(currentTime));
|
||||
if (msg.Attachments.Any())
|
||||
embed.AddField(efb => efb.WithName("Attachments").WithValue(string.Join(", ", msg.Attachments.Select(a => a.ProxyUrl))).WithIsInline(false));
|
||||
embed.AddField(efb => efb.WithName(logChannel.Guild.GetLogText("attachments")).WithValue(string.Join(", ", msg.Attachments.Select(a => a.Url))).WithIsInline(false));
|
||||
|
||||
await logChannel.EmbedAsync(embed).ConfigureAwait(false);
|
||||
}
|
||||
catch { }
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.Warn(ex);
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task _client_MessageUpdated(Optional<SocketMessage> optmsg, SocketMessage imsg2)
|
||||
@ -697,16 +751,19 @@ namespace NadekoBot.Modules.Administration
|
||||
|
||||
var embed = new EmbedBuilder()
|
||||
.WithOkColor()
|
||||
.WithTitle($"📝 Message Updated in {((ITextChannel)after.Channel).Mention}")
|
||||
.WithTitle("📝 " + logChannel.Guild.GetLogText("msg_update", ((ITextChannel)after.Channel).Name))
|
||||
.WithDescription(after.Author.ToString())
|
||||
.AddField(efb => efb.WithName("Old Message").WithValue(before.Resolve(userHandling: TagHandling.FullName)).WithIsInline(false))
|
||||
.AddField(efb => efb.WithName("New Message").WithValue(after.Resolve(userHandling: TagHandling.FullName)).WithIsInline(false))
|
||||
.AddField(efb => efb.WithName(logChannel.Guild.GetLogText("old_msg")).WithValue(string.IsNullOrWhiteSpace(before.Content) ? "-" : before.Resolve(userHandling: TagHandling.FullName)).WithIsInline(false))
|
||||
.AddField(efb => efb.WithName(logChannel.Guild.GetLogText("new_msg")).WithValue(string.IsNullOrWhiteSpace(after.Content) ? "-" : after.Resolve(userHandling: TagHandling.FullName)).WithIsInline(false))
|
||||
.AddField(efb => efb.WithName("Id").WithValue(after.Id.ToString()).WithIsInline(false))
|
||||
.WithFooter(efb => efb.WithText(currentTime));
|
||||
|
||||
await logChannel.EmbedAsync(embed).ConfigureAwait(false);
|
||||
}
|
||||
catch { }
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
public enum LogType
|
||||
@ -778,8 +835,6 @@ namespace NadekoBot.Modules.Administration
|
||||
case LogType.UserMuted:
|
||||
id = logSetting.UserMutedId;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!id.HasValue)
|
||||
@ -850,8 +905,6 @@ namespace NadekoBot.Modules.Administration
|
||||
case LogType.VoicePresenceTTS:
|
||||
newLogSetting.LogVoicePresenceTTSId = null;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
GuildLogSettings.AddOrUpdate(guildId, newLogSetting, (gid, old) => newLogSetting);
|
||||
uow.Complete();
|
||||
@ -895,9 +948,9 @@ namespace NadekoBot.Modules.Administration
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
if (action.Value)
|
||||
await channel.SendConfirmAsync("Logging all events in this channel.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("log_all").ConfigureAwait(false);
|
||||
else
|
||||
await channel.SendConfirmAsync("Logging disabled.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("log_disabled").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -924,9 +977,9 @@ namespace NadekoBot.Modules.Administration
|
||||
}
|
||||
|
||||
if (removed == 0)
|
||||
await channel.SendConfirmAsync($"Logging will IGNORE **{channel.Mention} ({channel.Id})**").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("log_ignore", Format.Bold(channel.Mention + "(" + channel.Id + ")")).ConfigureAwait(false);
|
||||
else
|
||||
await channel.SendConfirmAsync($"Logging will NOT IGNORE **{channel.Mention} ({channel.Id})**").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("log_not_ignore", Format.Bold(channel.Mention + "(" + channel.Id + ")")).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -935,7 +988,9 @@ namespace NadekoBot.Modules.Administration
|
||||
[OwnerOnly]
|
||||
public async Task LogEvents()
|
||||
{
|
||||
await Context.Channel.SendConfirmAsync("Log events you can subscribe to:", String.Join(", ", Enum.GetNames(typeof(LogType)).Cast<string>()));
|
||||
await Context.Channel.SendConfirmAsync(GetText("log_events") + "\n" +
|
||||
string.Join(", ", Enum.GetNames(typeof(LogType)).Cast<string>()))
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -1003,10 +1058,19 @@ namespace NadekoBot.Modules.Administration
|
||||
}
|
||||
|
||||
if (channelId != null)
|
||||
await channel.SendConfirmAsync($"Logging **{type}** event in this channel.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("log", Format.Bold(type.ToString())).ConfigureAwait(false);
|
||||
else
|
||||
await channel.SendConfirmAsync($"Stopped logging **{type}** event.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("log_stop", Format.Bold(type.ToString())).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class GuildExtensions
|
||||
{
|
||||
public static string GetLogText(this IGuild guild, string key, params object[] replacements)
|
||||
=> NadekoTopLevelModule.GetTextStatic(key,
|
||||
NadekoBot.Localization.GetCultureInfo(guild),
|
||||
typeof(Administration).Name.ToLowerInvariant(),
|
||||
replacements);
|
||||
}
|
||||
}
|
@ -20,11 +20,11 @@ namespace NadekoBot.Modules.Administration
|
||||
public partial class Administration
|
||||
{
|
||||
[Group]
|
||||
public class Migration : ModuleBase
|
||||
public class Migration : NadekoSubmodule
|
||||
{
|
||||
private const int CURRENT_VERSION = 1;
|
||||
|
||||
private static Logger _log { get; }
|
||||
private new static readonly Logger _log;
|
||||
|
||||
static Migration()
|
||||
{
|
||||
@ -51,12 +51,12 @@ namespace NadekoBot.Modules.Administration
|
||||
break;
|
||||
}
|
||||
}
|
||||
await Context.Channel.SendMessageAsync("🆙 **Migration done.**").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("migration_done").ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.Error(ex);
|
||||
await Context.Channel.SendMessageAsync("⚠️ **Error while migrating, check `logs` for more informations.**").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("migration_error").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -103,11 +103,8 @@ namespace NadekoBot.Modules.Administration
|
||||
var greetChannel = (ulong)(long)reader["GreetChannelId"];
|
||||
var greetMsg = (string)reader["GreetText"];
|
||||
var bye = (long)reader["Bye"] == 1;
|
||||
var byeDM = (long)reader["ByePM"] == 1;
|
||||
var byeChannel = (ulong)(long)reader["ByeChannelId"];
|
||||
var byeMsg = (string)reader["ByeText"];
|
||||
var grdel = false;
|
||||
var byedel = grdel;
|
||||
var gc = uow.GuildConfigs.For(gid, set => set);
|
||||
|
||||
if (greetDM)
|
||||
@ -121,7 +118,6 @@ namespace NadekoBot.Modules.Administration
|
||||
gc.ByeMessageChannelId = byeChannel;
|
||||
gc.ChannelByeMessageText = byeMsg;
|
||||
|
||||
gc.AutoDeleteGreetMessagesTimer = gc.AutoDeleteByeMessagesTimer = grdel ? 30 : 0;
|
||||
_log.Info(++i);
|
||||
}
|
||||
}
|
||||
@ -129,12 +125,12 @@ namespace NadekoBot.Modules.Administration
|
||||
_log.Warn("Greet/bye messages won't be migrated");
|
||||
}
|
||||
var com2 = db.CreateCommand();
|
||||
com.CommandText = "SELECT * FROM CurrencyState GROUP BY UserId";
|
||||
com2.CommandText = "SELECT * FROM CurrencyState GROUP BY UserId";
|
||||
|
||||
i = 0;
|
||||
try
|
||||
{
|
||||
var reader2 = com.ExecuteReader();
|
||||
var reader2 = com2.ExecuteReader();
|
||||
while (reader2.Read())
|
||||
{
|
||||
_log.Info(++i);
|
||||
@ -203,7 +199,6 @@ namespace NadekoBot.Modules.Administration
|
||||
guildConfig.ExclusiveSelfAssignedRoles = data.ExclusiveSelfAssignedRoles;
|
||||
guildConfig.GenerateCurrencyChannelIds = new HashSet<GCChannelId>(data.GenerateCurrencyChannels.Select(gc => new GCChannelId() { ChannelId = gc.Key }));
|
||||
selfAssRoles.AddRange(data.ListOfSelfAssignableRoles.Select(r => new SelfAssignedRole() { GuildId = guildConfig.GuildId, RoleId = r }).ToArray());
|
||||
var logSetting = guildConfig.LogSetting;
|
||||
guildConfig.LogSetting.IgnoredChannels = new HashSet<IgnoredLogChannel>(data.LogserverIgnoreChannels.Select(id => new IgnoredLogChannel() { ChannelId = id }));
|
||||
|
||||
guildConfig.LogSetting.LogUserPresenceId = data.LogPresenceChannel;
|
||||
@ -249,7 +244,7 @@ namespace NadekoBot.Modules.Administration
|
||||
|
||||
private void MigratePermissions0_9(IUnitOfWork uow)
|
||||
{
|
||||
var PermissionsDict = new ConcurrentDictionary<ulong, ServerPermissions0_9>();
|
||||
var permissionsDict = new ConcurrentDictionary<ulong, ServerPermissions0_9>();
|
||||
if (!Directory.Exists("data/permissions/"))
|
||||
{
|
||||
_log.Warn("No data from permissions will be migrated.");
|
||||
@ -263,12 +258,15 @@ namespace NadekoBot.Modules.Administration
|
||||
if (string.IsNullOrWhiteSpace(strippedFileName)) continue;
|
||||
var id = ulong.Parse(strippedFileName);
|
||||
var data = JsonConvert.DeserializeObject<ServerPermissions0_9>(File.ReadAllText(file));
|
||||
PermissionsDict.TryAdd(id, data);
|
||||
permissionsDict.TryAdd(id, data);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
var i = 0;
|
||||
PermissionsDict
|
||||
permissionsDict
|
||||
.Select(p => new { data = p.Value, gconfig = uow.GuildConfigs.For(p.Key) })
|
||||
.AsParallel()
|
||||
.ForAll(perms =>
|
||||
|
@ -8,8 +8,6 @@ using NadekoBot.Services.Database.Models;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@ -18,11 +16,10 @@ namespace NadekoBot.Modules.Administration
|
||||
public partial class Administration
|
||||
{
|
||||
[Group]
|
||||
public class MuteCommands : ModuleBase
|
||||
public class MuteCommands : NadekoSubmodule
|
||||
{
|
||||
private static ConcurrentDictionary<ulong, string> GuildMuteRoles { get; } = new ConcurrentDictionary<ulong, string>();
|
||||
|
||||
private static ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>> MutedUsers { get; } = new ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>>();
|
||||
private static ConcurrentDictionary<ulong, string> guildMuteRoles { get; }
|
||||
private static ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>> mutedUsers { get; }
|
||||
|
||||
public static event Action<IGuildUser, MuteType> UserMuted = delegate { };
|
||||
public static event Action<IGuildUser, MuteType> UserUnmuted = delegate { };
|
||||
@ -36,14 +33,12 @@ namespace NadekoBot.Modules.Administration
|
||||
|
||||
static MuteCommands()
|
||||
{
|
||||
var _log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
var configs = NadekoBot.AllGuildConfigs;
|
||||
GuildMuteRoles = new ConcurrentDictionary<ulong, string>(configs
|
||||
guildMuteRoles = new ConcurrentDictionary<ulong, string>(configs
|
||||
.Where(c => !string.IsNullOrWhiteSpace(c.MuteRoleName))
|
||||
.ToDictionary(c => c.GuildId, c => c.MuteRoleName));
|
||||
|
||||
MutedUsers = new ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>>(configs.ToDictionary(
|
||||
mutedUsers = new ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>>(configs.ToDictionary(
|
||||
k => k.GuildId,
|
||||
v => new ConcurrentHashSet<ulong>(v.MutedUsers.Select(m => m.UserId))
|
||||
));
|
||||
@ -56,16 +51,15 @@ namespace NadekoBot.Modules.Administration
|
||||
try
|
||||
{
|
||||
ConcurrentHashSet<ulong> muted;
|
||||
MutedUsers.TryGetValue(usr.Guild.Id, out muted);
|
||||
mutedUsers.TryGetValue(usr.Guild.Id, out muted);
|
||||
|
||||
if (muted == null || !muted.Contains(usr.Id))
|
||||
return;
|
||||
else
|
||||
await MuteUser(usr).ConfigureAwait(false);
|
||||
await MuteUser(usr).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.Warn(ex);
|
||||
LogManager.GetCurrentClassLogger().Warn(ex);
|
||||
}
|
||||
|
||||
}
|
||||
@ -82,7 +76,7 @@ namespace NadekoBot.Modules.Administration
|
||||
UserId = usr.Id
|
||||
});
|
||||
ConcurrentHashSet<ulong> muted;
|
||||
if (MutedUsers.TryGetValue(usr.Guild.Id, out muted))
|
||||
if (mutedUsers.TryGetValue(usr.Guild.Id, out muted))
|
||||
muted.Add(usr.Id);
|
||||
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
@ -102,18 +96,18 @@ namespace NadekoBot.Modules.Administration
|
||||
UserId = usr.Id
|
||||
});
|
||||
ConcurrentHashSet<ulong> muted;
|
||||
if (MutedUsers.TryGetValue(usr.Guild.Id, out muted))
|
||||
if (mutedUsers.TryGetValue(usr.Guild.Id, out muted))
|
||||
muted.TryRemove(usr.Id);
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
UserUnmuted(usr, MuteType.All);
|
||||
}
|
||||
|
||||
public static async Task<IRole> GetMuteRole(IGuild guild)
|
||||
public static async Task<IRole>GetMuteRole(IGuild guild)
|
||||
{
|
||||
const string defaultMuteRoleName = "nadeko-mute";
|
||||
|
||||
var muteRoleName = GuildMuteRoles.GetOrAdd(guild.Id, defaultMuteRoleName);
|
||||
var muteRoleName = guildMuteRoles.GetOrAdd(guild.Id, defaultMuteRoleName);
|
||||
|
||||
var muteRole = guild.Roles.FirstOrDefault(r => r.Name == muteRoleName);
|
||||
if (muteRole == null)
|
||||
@ -135,7 +129,10 @@ namespace NadekoBot.Modules.Administration
|
||||
await toOverwrite.AddPermissionOverwriteAsync(muteRole, new OverwritePermissions(sendMessages: PermValue.Deny, attachFiles: PermValue.Deny))
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
catch { }
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
await Task.Delay(200).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@ -148,7 +145,6 @@ namespace NadekoBot.Modules.Administration
|
||||
[Priority(1)]
|
||||
public async Task SetMuteRole([Remainder] string name)
|
||||
{
|
||||
//var channel = (ITextChannel)Context.Channel;
|
||||
name = name.Trim();
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
return;
|
||||
@ -157,10 +153,10 @@ namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
var config = uow.GuildConfigs.For(Context.Guild.Id, set => set);
|
||||
config.MuteRoleName = name;
|
||||
GuildMuteRoles.AddOrUpdate(Context.Guild.Id, name, (id, old) => name);
|
||||
guildMuteRoles.AddOrUpdate(Context.Guild.Id, name, (id, old) => name);
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
await Context.Channel.SendConfirmAsync("☑️ **New mute role set.**").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("mute_role_set").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -178,12 +174,12 @@ namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
try
|
||||
{
|
||||
await MuteUser(user).ConfigureAwait(false);
|
||||
await Context.Channel.SendConfirmAsync($"🔇 **{user}** has been **muted** from text and voice chat.").ConfigureAwait(false);
|
||||
await MuteUser(user).ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("user_muted", Format.Bold(user.ToString())).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("⚠️ I most likely don't have the permission necessary for that.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("mute_error").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -196,11 +192,11 @@ namespace NadekoBot.Modules.Administration
|
||||
try
|
||||
{
|
||||
await UnmuteUser(user).ConfigureAwait(false);
|
||||
await Context.Channel.SendConfirmAsync($"🔉 **{user}** has been **unmuted** from text and voice chat.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("user_unmuted", Format.Bold(user.ToString())).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("⚠️ I most likely don't have the permission necessary for that.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("mute_error").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -213,11 +209,11 @@ namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
await user.AddRolesAsync(await GetMuteRole(Context.Guild).ConfigureAwait(false)).ConfigureAwait(false);
|
||||
UserMuted(user, MuteType.Chat);
|
||||
await Context.Channel.SendConfirmAsync($"✏️🚫 **{user}** has been **muted** from chatting.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("user_chat_mute", Format.Bold(user.ToString())).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("⚠️ I most likely don't have the permission necessary for that.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("mute_error").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -230,11 +226,11 @@ namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
await user.RemoveRolesAsync(await GetMuteRole(Context.Guild).ConfigureAwait(false)).ConfigureAwait(false);
|
||||
UserUnmuted(user, MuteType.Chat);
|
||||
await Context.Channel.SendConfirmAsync($"✏️✅ **{user}** has been **unmuted** from chatting.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("user_chat_unmute", Format.Bold(user.ToString())).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("⚠️ I most likely don't have the permission necessary for that.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("mute_error").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -247,11 +243,11 @@ namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
await user.ModifyAsync(usr => usr.Mute = true).ConfigureAwait(false);
|
||||
UserMuted(user, MuteType.Voice);
|
||||
await Context.Channel.SendConfirmAsync($"🎙🚫 **{user}** has been **voice muted**.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("user_voice_mute", Format.Bold(user.ToString())).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("⚠️ I most likely don't have the permission necessary for that.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("mute_error").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -264,11 +260,11 @@ namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
await user.ModifyAsync(usr => usr.Mute = false).ConfigureAwait(false);
|
||||
UserUnmuted(user, MuteType.Voice);
|
||||
await Context.Channel.SendConfirmAsync($"🎙✅ **{user}** has been **voice unmuted**.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("user_voice_unmute", Format.Bold(user.ToString())).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("⚠️ I most likely don't have the permission necessary for that.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("mute_error").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using Discord.Commands;
|
||||
using Discord.WebSocket;
|
||||
using NadekoBot.Attributes;
|
||||
using NadekoBot.Extensions;
|
||||
@ -8,7 +7,6 @@ using NadekoBot.Services.Database.Models;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@ -18,13 +16,19 @@ namespace NadekoBot.Modules.Administration
|
||||
public partial class Administration
|
||||
{
|
||||
[Group]
|
||||
public class PlayingRotateCommands : ModuleBase
|
||||
public class PlayingRotateCommands : NadekoSubmodule
|
||||
{
|
||||
private static Logger _log { get; }
|
||||
public static List<PlayingStatus> RotatingStatusMessages { get; }
|
||||
public static bool RotatingStatuses { get; private set; } = false;
|
||||
private static Timer _t { get; }
|
||||
|
||||
public static volatile bool RotatingStatuses;
|
||||
private readonly object _locker = new object();
|
||||
private new static Logger _log { get; }
|
||||
private static readonly Timer _t;
|
||||
|
||||
private class TimerState
|
||||
{
|
||||
public int Index { get; set; }
|
||||
}
|
||||
|
||||
static PlayingRotateCommands()
|
||||
{
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
@ -32,35 +36,31 @@ namespace NadekoBot.Modules.Administration
|
||||
RotatingStatusMessages = NadekoBot.BotConfig.RotatingStatusMessages;
|
||||
RotatingStatuses = NadekoBot.BotConfig.RotatingStatuses;
|
||||
|
||||
|
||||
|
||||
_t = new Timer(async (_) =>
|
||||
_t = new Timer(async (objState) =>
|
||||
{
|
||||
var index = 0;
|
||||
try
|
||||
{
|
||||
var state = (TimerState)objState;
|
||||
if (!RotatingStatuses)
|
||||
return;
|
||||
else
|
||||
{
|
||||
if (index >= RotatingStatusMessages.Count)
|
||||
index = 0;
|
||||
if (state.Index >= RotatingStatusMessages.Count)
|
||||
state.Index = 0;
|
||||
|
||||
if (!RotatingStatusMessages.Any())
|
||||
return;
|
||||
var status = RotatingStatusMessages[index++].Status;
|
||||
if (string.IsNullOrWhiteSpace(status))
|
||||
return;
|
||||
PlayingPlaceholders.ForEach(e => status = status.Replace(e.Key, e.Value()));
|
||||
var shards = NadekoBot.Client.Shards;
|
||||
for (int i = 0; i < shards.Count; i++)
|
||||
if (!RotatingStatusMessages.Any())
|
||||
return;
|
||||
var status = RotatingStatusMessages[state.Index++].Status;
|
||||
if (string.IsNullOrWhiteSpace(status))
|
||||
return;
|
||||
PlayingPlaceholders.ForEach(e => status = status.Replace(e.Key, e.Value()));
|
||||
var shards = NadekoBot.Client.Shards;
|
||||
for (int i = 0; i < shards.Count; i++)
|
||||
{
|
||||
var curShard = shards.ElementAt(i);
|
||||
ShardSpecificPlaceholders.ForEach(e => status = status.Replace(e.Key, e.Value(curShard)));
|
||||
try { await shards.ElementAt(i).SetGameAsync(status).ConfigureAwait(false); }
|
||||
catch (Exception ex)
|
||||
{
|
||||
ShardSpecificPlaceholders.ForEach(e => status = status.Replace(e.Key, e.Value(shards.ElementAt(i))));
|
||||
try { await shards.ElementAt(i).SetGameAsync(status).ConfigureAwait(false); }
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.Warn(ex);
|
||||
}
|
||||
_log.Warn(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -68,7 +68,7 @@ namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
_log.Warn("Rotating playing status errored.\n" + ex);
|
||||
}
|
||||
}, null, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1));
|
||||
}, new TimerState(), TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1));
|
||||
}
|
||||
|
||||
public static Dictionary<string, Func<string>> PlayingPlaceholders { get; } =
|
||||
@ -102,17 +102,20 @@ namespace NadekoBot.Modules.Administration
|
||||
[OwnerOnly]
|
||||
public async Task RotatePlaying()
|
||||
{
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
lock (_locker)
|
||||
{
|
||||
var config = uow.BotConfig.GetOrCreate();
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
{
|
||||
var config = uow.BotConfig.GetOrCreate();
|
||||
|
||||
RotatingStatuses = config.RotatingStatuses = !config.RotatingStatuses;
|
||||
await uow.CompleteAsync();
|
||||
RotatingStatuses = config.RotatingStatuses = !config.RotatingStatuses;
|
||||
uow.Complete();
|
||||
}
|
||||
}
|
||||
if (RotatingStatuses)
|
||||
await Context.Channel.SendConfirmAsync("🆗 **Rotating playing status enabled.**").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("ropl_enabled").ConfigureAwait(false);
|
||||
else
|
||||
await Context.Channel.SendConfirmAsync("ℹ️ **Rotating playing status disabled.**").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("ropl_disabled").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -128,7 +131,7 @@ namespace NadekoBot.Modules.Administration
|
||||
await uow.CompleteAsync();
|
||||
}
|
||||
|
||||
await Context.Channel.SendConfirmAsync("✅ **Added.**").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("ropl_added").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -136,11 +139,13 @@ namespace NadekoBot.Modules.Administration
|
||||
public async Task ListPlaying()
|
||||
{
|
||||
if (!RotatingStatusMessages.Any())
|
||||
await Context.Channel.SendErrorAsync("❎ **No rotating playing statuses set.**");
|
||||
await ReplyErrorLocalized("ropl_not_set").ConfigureAwait(false);
|
||||
else
|
||||
{
|
||||
var i = 1;
|
||||
await Context.Channel.SendConfirmAsync($"ℹ️ {Context.User.Mention} `Here is a list of rotating statuses:`\n\n\t" + string.Join("\n\t", RotatingStatusMessages.Select(rs => $"`{i++}.` {rs.Status}")));
|
||||
await ReplyConfirmLocalized("ropl_list",
|
||||
string.Join("\n\t", RotatingStatusMessages.Select(rs => $"`{i++}.` {rs.Status}")))
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
}
|
||||
@ -151,7 +156,7 @@ namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
index -= 1;
|
||||
|
||||
string msg = "";
|
||||
string msg;
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
{
|
||||
var config = uow.BotConfig.GetOrCreate();
|
||||
@ -163,7 +168,7 @@ namespace NadekoBot.Modules.Administration
|
||||
RotatingStatusMessages.RemoveAt(index);
|
||||
await uow.CompleteAsync();
|
||||
}
|
||||
await Context.Channel.SendConfirmAsync($"🗑 **Removed the the playing message:** {msg}").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("reprm", msg).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,12 +4,10 @@ using Microsoft.EntityFrameworkCore;
|
||||
using NadekoBot.Attributes;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Services;
|
||||
using NadekoBot.Services.Database;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@ -26,12 +24,8 @@ namespace NadekoBot.Modules.Administration
|
||||
public class AntiRaidStats
|
||||
{
|
||||
public AntiRaidSetting AntiRaidSettings { get; set; }
|
||||
public int UsersCount { get; set; } = 0;
|
||||
public int UsersCount { get; set; }
|
||||
public ConcurrentHashSet<IGuildUser> RaidUsers { get; set; } = new ConcurrentHashSet<IGuildUser>();
|
||||
|
||||
public override string ToString() =>
|
||||
$"If **{AntiRaidSettings.UserThreshold}** or more users join within **{AntiRaidSettings.Seconds}** seconds," +
|
||||
$" I will **{AntiRaidSettings.Action}** them.";
|
||||
}
|
||||
|
||||
public class AntiSpamStats
|
||||
@ -39,16 +33,6 @@ namespace NadekoBot.Modules.Administration
|
||||
public AntiSpamSetting AntiSpamSettings { get; set; }
|
||||
public ConcurrentDictionary<ulong, UserSpamStats> UserStats { get; set; }
|
||||
= new ConcurrentDictionary<ulong, UserSpamStats>();
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var ignoredString = string.Join(", ", AntiSpamSettings.IgnoredChannels.Select(c => $"<#{c.ChannelId}>"));
|
||||
|
||||
if (string.IsNullOrWhiteSpace(ignoredString))
|
||||
ignoredString = "none";
|
||||
return $"If a user posts **{AntiSpamSettings.MessageThreshold}** same messages in a row, I will **{AntiSpamSettings.Action}** them."
|
||||
+ $"\n\t__IgnoredChannels__: {ignoredString}";
|
||||
}
|
||||
}
|
||||
|
||||
public class UserSpamStats
|
||||
@ -76,15 +60,15 @@ namespace NadekoBot.Modules.Administration
|
||||
}
|
||||
|
||||
[Group]
|
||||
public class ProtectionCommands : ModuleBase
|
||||
public class ProtectionCommands : NadekoSubmodule
|
||||
{
|
||||
private static ConcurrentDictionary<ulong, AntiRaidStats> antiRaidGuilds =
|
||||
private static readonly ConcurrentDictionary<ulong, AntiRaidStats> _antiRaidGuilds =
|
||||
new ConcurrentDictionary<ulong, AntiRaidStats>();
|
||||
// guildId | (userId|messages)
|
||||
private static ConcurrentDictionary<ulong, AntiSpamStats> antiSpamGuilds =
|
||||
private static readonly ConcurrentDictionary<ulong, AntiSpamStats> _antiSpamGuilds =
|
||||
new ConcurrentDictionary<ulong, AntiSpamStats>();
|
||||
|
||||
private static Logger _log { get; }
|
||||
private new static readonly Logger _log;
|
||||
|
||||
static ProtectionCommands()
|
||||
{
|
||||
@ -98,81 +82,98 @@ namespace NadekoBot.Modules.Administration
|
||||
if (raid != null)
|
||||
{
|
||||
var raidStats = new AntiRaidStats() { AntiRaidSettings = raid };
|
||||
antiRaidGuilds.TryAdd(gc.GuildId, raidStats);
|
||||
_antiRaidGuilds.TryAdd(gc.GuildId, raidStats);
|
||||
}
|
||||
|
||||
if (spam != null)
|
||||
antiSpamGuilds.TryAdd(gc.GuildId, new AntiSpamStats() { AntiSpamSettings = spam });
|
||||
_antiSpamGuilds.TryAdd(gc.GuildId, new AntiSpamStats() { AntiSpamSettings = spam });
|
||||
}
|
||||
|
||||
NadekoBot.Client.MessageReceived += async (imsg) =>
|
||||
NadekoBot.Client.MessageReceived += (imsg) =>
|
||||
{
|
||||
var msg = imsg as IUserMessage;
|
||||
if (msg == null || msg.Author.IsBot)
|
||||
return Task.CompletedTask;
|
||||
|
||||
try
|
||||
var channel = msg.Channel as ITextChannel;
|
||||
if (channel == null)
|
||||
return Task.CompletedTask;
|
||||
var _ = Task.Run(async () =>
|
||||
{
|
||||
var msg = imsg as IUserMessage;
|
||||
if (msg == null || msg.Author.IsBot)
|
||||
return;
|
||||
|
||||
var channel = msg.Channel as ITextChannel;
|
||||
if (channel == null)
|
||||
return;
|
||||
AntiSpamStats spamSettings;
|
||||
if (!antiSpamGuilds.TryGetValue(channel.Guild.Id, out spamSettings) ||
|
||||
spamSettings.AntiSpamSettings.IgnoredChannels.Contains(new AntiSpamIgnore()
|
||||
{
|
||||
ChannelId = channel.Id
|
||||
}))
|
||||
return;
|
||||
|
||||
var stats = spamSettings.UserStats.AddOrUpdate(msg.Author.Id, new UserSpamStats(msg.Content),
|
||||
(id, old) => { old.ApplyNextMessage(msg.Content); return old; });
|
||||
|
||||
if (stats.Count >= spamSettings.AntiSpamSettings.MessageThreshold)
|
||||
try
|
||||
{
|
||||
if (spamSettings.UserStats.TryRemove(msg.Author.Id, out stats))
|
||||
AntiSpamStats spamSettings;
|
||||
if (!_antiSpamGuilds.TryGetValue(channel.Guild.Id, out spamSettings) ||
|
||||
spamSettings.AntiSpamSettings.IgnoredChannels.Contains(new AntiSpamIgnore()
|
||||
{
|
||||
ChannelId = channel.Id
|
||||
}))
|
||||
return;
|
||||
|
||||
var stats = spamSettings.UserStats.AddOrUpdate(msg.Author.Id, new UserSpamStats(msg.Content),
|
||||
(id, old) =>
|
||||
{
|
||||
old.ApplyNextMessage(msg.Content); return old;
|
||||
});
|
||||
|
||||
if (stats.Count >= spamSettings.AntiSpamSettings.MessageThreshold)
|
||||
{
|
||||
await PunishUsers(spamSettings.AntiSpamSettings.Action, ProtectionType.Spamming, (IGuildUser)msg.Author)
|
||||
.ConfigureAwait(false);
|
||||
if (spamSettings.UserStats.TryRemove(msg.Author.Id, out stats))
|
||||
{
|
||||
await PunishUsers(spamSettings.AntiSpamSettings.Action, ProtectionType.Spamming, (IGuildUser)msg.Author)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
});
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
NadekoBot.Client.UserJoined += async (usr) =>
|
||||
NadekoBot.Client.UserJoined += (usr) =>
|
||||
{
|
||||
try
|
||||
if (usr.IsBot)
|
||||
return Task.CompletedTask;
|
||||
AntiRaidStats settings;
|
||||
if (!_antiRaidGuilds.TryGetValue(usr.Guild.Id, out settings))
|
||||
return Task.CompletedTask;
|
||||
if (!settings.RaidUsers.Add(usr))
|
||||
return Task.CompletedTask;
|
||||
|
||||
var _ = Task.Run(async () =>
|
||||
{
|
||||
if (usr.IsBot)
|
||||
return;
|
||||
AntiRaidStats settings;
|
||||
if (!antiRaidGuilds.TryGetValue(usr.Guild.Id, out settings))
|
||||
return;
|
||||
if (!settings.RaidUsers.Add(usr))
|
||||
return;
|
||||
|
||||
++settings.UsersCount;
|
||||
|
||||
if (settings.UsersCount >= settings.AntiRaidSettings.UserThreshold)
|
||||
try
|
||||
{
|
||||
var users = settings.RaidUsers.ToArray();
|
||||
settings.RaidUsers.Clear();
|
||||
++settings.UsersCount;
|
||||
|
||||
if (settings.UsersCount >= settings.AntiRaidSettings.UserThreshold)
|
||||
{
|
||||
var users = settings.RaidUsers.ToArray();
|
||||
settings.RaidUsers.Clear();
|
||||
|
||||
await PunishUsers(settings.AntiRaidSettings.Action, ProtectionType.Raiding, users).ConfigureAwait(false);
|
||||
}
|
||||
await Task.Delay(1000 * settings.AntiRaidSettings.Seconds).ConfigureAwait(false);
|
||||
|
||||
settings.RaidUsers.TryRemove(usr);
|
||||
--settings.UsersCount;
|
||||
|
||||
await PunishUsers(settings.AntiRaidSettings.Action, ProtectionType.Raiding, users).ConfigureAwait(false);
|
||||
}
|
||||
await Task.Delay(1000 * settings.AntiRaidSettings.Seconds).ConfigureAwait(false);
|
||||
|
||||
settings.RaidUsers.TryRemove(usr);
|
||||
--settings.UsersCount;
|
||||
|
||||
}
|
||||
catch { }
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
});
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
}
|
||||
|
||||
private static async Task PunishUsers(PunishmentAction action, ProtectionType pt, params IGuildUser[] gus)
|
||||
{
|
||||
_log.Info($"[{pt}] - Punishing [{gus.Length}] users with [{action}] in {gus[0].Guild.Name} guild");
|
||||
foreach (var gu in gus)
|
||||
{
|
||||
switch (action)
|
||||
@ -208,13 +209,27 @@ namespace NadekoBot.Modules.Administration
|
||||
}
|
||||
catch (Exception ex) { _log.Warn(ex, "I can't apply punishment"); }
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
await LogCommands.TriggeredAntiProtection(gus, action, pt).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private string GetAntiSpamString(AntiSpamStats stats)
|
||||
{
|
||||
var ignoredString = string.Join(", ", stats.AntiSpamSettings.IgnoredChannels.Select(c => $"<#{c.ChannelId}>"));
|
||||
|
||||
if (string.IsNullOrWhiteSpace(ignoredString))
|
||||
ignoredString = "none";
|
||||
return GetText("spam_stats",
|
||||
Format.Bold(stats.AntiSpamSettings.MessageThreshold.ToString()),
|
||||
Format.Bold(stats.AntiSpamSettings.Action.ToString()),
|
||||
ignoredString);
|
||||
}
|
||||
|
||||
private string GetAntiRaidString(AntiRaidStats stats) => GetText("raid_stats",
|
||||
Format.Bold(stats.AntiRaidSettings.UserThreshold.ToString()),
|
||||
Format.Bold(stats.AntiRaidSettings.Seconds.ToString()),
|
||||
Format.Bold(stats.AntiRaidSettings.Action.ToString()));
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
@ -223,18 +238,18 @@ namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
if (userThreshold < 2 || userThreshold > 30)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("❗️User threshold must be between **2** and **30**.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("raid_cnt", 2, 30).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (seconds < 2 || seconds > 300)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("❗️Time must be between **2** and **300** seconds.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("raid_time", 2, 300).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
AntiRaidStats throwaway;
|
||||
if (antiRaidGuilds.TryRemove(Context.Guild.Id, out throwaway))
|
||||
if (_antiRaidGuilds.TryRemove(Context.Guild.Id, out throwaway))
|
||||
{
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
{
|
||||
@ -243,7 +258,7 @@ namespace NadekoBot.Modules.Administration
|
||||
gc.AntiRaidSetting = null;
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
await Context.Channel.SendConfirmAsync("**Anti-Raid** feature has been **disabled** on this server.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("prot_disable", "Anti-Raid").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -253,10 +268,8 @@ namespace NadekoBot.Modules.Administration
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await Context.Channel.SendConfirmAsync("⚠️ Failed creating a mute role. Give me ManageRoles permission" +
|
||||
"or create 'nadeko-mute' role with disabled SendMessages and try again.")
|
||||
.ConfigureAwait(false);
|
||||
_log.Warn(ex);
|
||||
await ReplyErrorLocalized("prot_error").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -270,7 +283,7 @@ namespace NadekoBot.Modules.Administration
|
||||
}
|
||||
};
|
||||
|
||||
antiRaidGuilds.AddOrUpdate(Context.Guild.Id, stats, (key, old) => stats);
|
||||
_antiRaidGuilds.AddOrUpdate(Context.Guild.Id, stats, (key, old) => stats);
|
||||
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
{
|
||||
@ -280,7 +293,7 @@ namespace NadekoBot.Modules.Administration
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await Context.Channel.SendConfirmAsync("Anti-Raid Enabled", $"{Context.User.Mention} {stats.ToString()}")
|
||||
await Context.Channel.SendConfirmAsync(GetText("prot_enable", "Anti-Raid"), $"{Context.User.Mention} {GetAntiRaidString(stats)}")
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@ -293,7 +306,7 @@ namespace NadekoBot.Modules.Administration
|
||||
return;
|
||||
|
||||
AntiSpamStats throwaway;
|
||||
if (antiSpamGuilds.TryRemove(Context.Guild.Id, out throwaway))
|
||||
if (_antiSpamGuilds.TryRemove(Context.Guild.Id, out throwaway))
|
||||
{
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
{
|
||||
@ -303,7 +316,7 @@ namespace NadekoBot.Modules.Administration
|
||||
gc.AntiSpamSetting = null;
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
await Context.Channel.SendConfirmAsync("**Anti-Spam** has been **disabled** on this server.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("prot_disable", "Anti-Spam").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -313,10 +326,8 @@ namespace NadekoBot.Modules.Administration
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("⚠️ Failed creating a mute role. Give me ManageRoles permission" +
|
||||
"or create 'nadeko-mute' role with disabled SendMessages and try again.")
|
||||
.ConfigureAwait(false);
|
||||
_log.Warn(ex);
|
||||
await ReplyErrorLocalized("prot_error").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -329,7 +340,7 @@ namespace NadekoBot.Modules.Administration
|
||||
}
|
||||
};
|
||||
|
||||
antiSpamGuilds.AddOrUpdate(Context.Guild.Id, stats, (key, old) => stats);
|
||||
_antiSpamGuilds.AddOrUpdate(Context.Guild.Id, stats, (key, old) => stats);
|
||||
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
{
|
||||
@ -339,7 +350,7 @@ namespace NadekoBot.Modules.Administration
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await Context.Channel.SendConfirmAsync("Anti-Spam Enabled", $"{Context.User.Mention} {stats.ToString()}").ConfigureAwait(false);
|
||||
await Context.Channel.SendConfirmAsync(GetText("prot_enable", "Anti-Spam"), $"{Context.User.Mention} {GetAntiSpamString(stats)}").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -365,7 +376,7 @@ namespace NadekoBot.Modules.Administration
|
||||
if (spam.IgnoredChannels.Add(obj))
|
||||
{
|
||||
AntiSpamStats temp;
|
||||
if (antiSpamGuilds.TryGetValue(Context.Guild.Id, out temp))
|
||||
if (_antiSpamGuilds.TryGetValue(Context.Guild.Id, out temp))
|
||||
temp.AntiSpamSettings.IgnoredChannels.Add(obj);
|
||||
added = true;
|
||||
}
|
||||
@ -373,7 +384,7 @@ namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
spam.IgnoredChannels.Remove(obj);
|
||||
AntiSpamStats temp;
|
||||
if (antiSpamGuilds.TryGetValue(Context.Guild.Id, out temp))
|
||||
if (_antiSpamGuilds.TryGetValue(Context.Guild.Id, out temp))
|
||||
temp.AntiSpamSettings.IgnoredChannels.Remove(obj);
|
||||
added = false;
|
||||
}
|
||||
@ -381,9 +392,9 @@ namespace NadekoBot.Modules.Administration
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
if (added)
|
||||
await Context.Channel.SendConfirmAsync("Anti-Spam will ignore this channel.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("spam_ignore", "Anti-Spam").ConfigureAwait(false);
|
||||
else
|
||||
await Context.Channel.SendConfirmAsync("Anti-Spam will no longer ignore this channel.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("spam_not_ignore", "Anti-Spam").ConfigureAwait(false);
|
||||
|
||||
}
|
||||
|
||||
@ -391,31 +402,29 @@ namespace NadekoBot.Modules.Administration
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task AntiList()
|
||||
{
|
||||
var channel = (ITextChannel)Context.Channel;
|
||||
|
||||
AntiSpamStats spam;
|
||||
antiSpamGuilds.TryGetValue(Context.Guild.Id, out spam);
|
||||
_antiSpamGuilds.TryGetValue(Context.Guild.Id, out spam);
|
||||
|
||||
AntiRaidStats raid;
|
||||
antiRaidGuilds.TryGetValue(Context.Guild.Id, out raid);
|
||||
_antiRaidGuilds.TryGetValue(Context.Guild.Id, out raid);
|
||||
|
||||
if (spam == null && raid == null)
|
||||
{
|
||||
await Context.Channel.SendConfirmAsync("No protections enabled.");
|
||||
await ReplyConfirmLocalized("prot_none").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
var embed = new EmbedBuilder().WithOkColor()
|
||||
.WithTitle("Protections Enabled");
|
||||
.WithTitle(GetText("prot_active"));
|
||||
|
||||
if (spam != null)
|
||||
embed.AddField(efb => efb.WithName("Anti-Spam")
|
||||
.WithValue(spam.ToString())
|
||||
.WithValue(GetAntiSpamString(spam))
|
||||
.WithIsInline(true));
|
||||
|
||||
if (raid != null)
|
||||
embed.AddField(efb => efb.WithName("Anti-Raid")
|
||||
.WithValue(raid.ToString())
|
||||
.WithValue(GetAntiRaidString(raid))
|
||||
.WithIsInline(true));
|
||||
|
||||
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
|
||||
|
@ -13,10 +13,10 @@ namespace NadekoBot.Modules.Administration
|
||||
public partial class Administration
|
||||
{
|
||||
[Group]
|
||||
public class RatelimitCommand : ModuleBase
|
||||
public class RatelimitCommand : NadekoSubmodule
|
||||
{
|
||||
public static ConcurrentDictionary<ulong, Ratelimiter> RatelimitingChannels = new ConcurrentDictionary<ulong, Ratelimiter>();
|
||||
private static Logger _log { get; }
|
||||
private new static readonly Logger _log;
|
||||
|
||||
public class Ratelimiter
|
||||
{
|
||||
@ -37,26 +37,22 @@ namespace NadekoBot.Modules.Administration
|
||||
|
||||
public bool CheckUserRatelimit(ulong id)
|
||||
{
|
||||
RatelimitedUser usr = Users.GetOrAdd(id, (key) => new RatelimitedUser() { UserId = id });
|
||||
var usr = Users.GetOrAdd(id, (key) => new RatelimitedUser() { UserId = id });
|
||||
if (usr.MessageCount == MaxMessages)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
usr.MessageCount++;
|
||||
var _ = Task.Run(async () =>
|
||||
{
|
||||
usr.MessageCount++;
|
||||
var t = Task.Run(async () =>
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
await Task.Delay(PerSeconds * 1000, cancelSource.Token);
|
||||
}
|
||||
catch (OperationCanceledException) { }
|
||||
usr.MessageCount--;
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
await Task.Delay(PerSeconds * 1000, cancelSource.Token);
|
||||
}
|
||||
catch (OperationCanceledException) { }
|
||||
usr.MessageCount--;
|
||||
});
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -69,9 +65,7 @@ namespace NadekoBot.Modules.Administration
|
||||
try
|
||||
{
|
||||
var usrMsg = umsg as IUserMessage;
|
||||
if (usrMsg == null)
|
||||
return;
|
||||
var channel = usrMsg.Channel as ITextChannel;
|
||||
var channel = usrMsg?.Channel as ITextChannel;
|
||||
|
||||
if (channel == null || usrMsg.IsAuthor())
|
||||
return;
|
||||
@ -95,8 +89,7 @@ namespace NadekoBot.Modules.Administration
|
||||
if (RatelimitingChannels.TryRemove(Context.Channel.Id, out throwaway))
|
||||
{
|
||||
throwaway.cancelSource.Cancel();
|
||||
await Context.Channel.SendConfirmAsync("ℹ️ Slow mode disabled.").ConfigureAwait(false);
|
||||
return;
|
||||
await ReplyConfirmLocalized("slowmode_disabled").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -109,7 +102,7 @@ namespace NadekoBot.Modules.Administration
|
||||
|
||||
if (msg < 1 || perSec < 1 || msg > 100 || perSec > 3600)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("⚠️ Invalid parameters.");
|
||||
await ReplyErrorLocalized("invalid_params").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
var toAdd = new Ratelimiter()
|
||||
@ -120,8 +113,8 @@ namespace NadekoBot.Modules.Administration
|
||||
};
|
||||
if(RatelimitingChannels.TryAdd(Context.Channel.Id, toAdd))
|
||||
{
|
||||
await Context.Channel.SendConfirmAsync("Slow mode initiated",
|
||||
$"Users can't send more than `{toAdd.MaxMessages} message(s)` every `{toAdd.PerSeconds} second(s)`.")
|
||||
await Context.Channel.SendConfirmAsync(GetText("slowmode_init"),
|
||||
GetText("slowmode_desc", Format.Bold(toAdd.MaxMessages.ToString()), Format.Bold(toAdd.PerSeconds.ToString())))
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ namespace NadekoBot.Modules.Administration
|
||||
public partial class Administration
|
||||
{
|
||||
[Group]
|
||||
public class SelfAssignedRolesCommands : ModuleBase
|
||||
public class SelfAssignedRolesCommands : NadekoSubmodule
|
||||
{
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -44,25 +44,30 @@ namespace NadekoBot.Modules.Administration
|
||||
IEnumerable<SelfAssignedRole> roles;
|
||||
|
||||
string msg;
|
||||
var error = false;
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
{
|
||||
roles = uow.SelfAssignedRoles.GetFromGuild(Context.Guild.Id);
|
||||
if (roles.Any(s => s.RoleId == role.Id && s.GuildId == role.Guild.Id))
|
||||
{
|
||||
await Context.Channel.SendMessageAsync($"💢 Role **{role.Name}** is already in the list.").ConfigureAwait(false);
|
||||
return;
|
||||
msg = GetText("role_in_list", Format.Bold(role.Name));
|
||||
error = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
uow.SelfAssignedRoles.Add(new SelfAssignedRole {
|
||||
uow.SelfAssignedRoles.Add(new SelfAssignedRole
|
||||
{
|
||||
RoleId = role.Id,
|
||||
GuildId = role.Guild.Id
|
||||
});
|
||||
await uow.CompleteAsync();
|
||||
msg = $"🆗 Role **{role.Name}** added to the list.";
|
||||
msg = GetText("role_added", Format.Bold(role.Name));
|
||||
}
|
||||
}
|
||||
await Context.Channel.SendConfirmAsync(msg.ToString()).ConfigureAwait(false);
|
||||
if (error)
|
||||
await Context.Channel.SendErrorAsync(msg).ConfigureAwait(false);
|
||||
else
|
||||
await Context.Channel.SendConfirmAsync(msg).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -70,8 +75,6 @@ namespace NadekoBot.Modules.Administration
|
||||
[RequireUserPermission(GuildPermission.ManageRoles)]
|
||||
public async Task Rsar([Remainder] IRole role)
|
||||
{
|
||||
//var channel = (ITextChannel)Context.Channel;
|
||||
|
||||
bool success;
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
{
|
||||
@ -80,18 +83,16 @@ namespace NadekoBot.Modules.Administration
|
||||
}
|
||||
if (!success)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("❎ That role is not self-assignable.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("self_assign_not").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
await Context.Channel.SendConfirmAsync($"🗑 **{role.Name}** has been removed from the list of self-assignable roles.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("self_assign_rem", Format.Bold(role.Name)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task Lsar()
|
||||
{
|
||||
//var channel = (ITextChannel)Context.Channel;
|
||||
|
||||
var toRemove = new ConcurrentHashSet<SelfAssignedRole>();
|
||||
var removeMsg = new StringBuilder();
|
||||
var msg = new StringBuilder();
|
||||
@ -116,11 +117,11 @@ namespace NadekoBot.Modules.Administration
|
||||
}
|
||||
foreach (var role in toRemove)
|
||||
{
|
||||
removeMsg.AppendLine($"`{role.RoleId} not found. Cleaned up.`");
|
||||
removeMsg.AppendLine(GetText("role_clean", role.RoleId));
|
||||
}
|
||||
await uow.CompleteAsync();
|
||||
}
|
||||
await Context.Channel.SendConfirmAsync($"ℹ️ There are `{roleCnt}` self assignable roles:", msg.ToString() + "\n\n" + removeMsg.ToString()).ConfigureAwait(false);
|
||||
await Context.Channel.SendConfirmAsync(GetText("self_assign_list", roleCnt), msg + "\n\n" + removeMsg).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -128,8 +129,6 @@ namespace NadekoBot.Modules.Administration
|
||||
[RequireUserPermission(GuildPermission.ManageRoles)]
|
||||
public async Task Tesar()
|
||||
{
|
||||
//var channel = (ITextChannel)Context.Channel;
|
||||
|
||||
bool areExclusive;
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
{
|
||||
@ -138,15 +137,16 @@ namespace NadekoBot.Modules.Administration
|
||||
areExclusive = config.ExclusiveSelfAssignedRoles = !config.ExclusiveSelfAssignedRoles;
|
||||
await uow.CompleteAsync();
|
||||
}
|
||||
string exl = areExclusive ? "**exclusive**." : "**not exclusive**.";
|
||||
await Context.Channel.SendConfirmAsync("ℹ️ Self assigned roles are now " + exl);
|
||||
if(areExclusive)
|
||||
await ReplyConfirmLocalized("self_assign_excl").ConfigureAwait(false);
|
||||
else
|
||||
await ReplyConfirmLocalized("self_assign_no_excl").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task Iam([Remainder] IRole role)
|
||||
{
|
||||
//var channel = (ITextChannel)Context.Channel;
|
||||
var guildUser = (IGuildUser)Context.User;
|
||||
|
||||
GuildConfig conf;
|
||||
@ -156,25 +156,24 @@ namespace NadekoBot.Modules.Administration
|
||||
conf = uow.GuildConfigs.For(Context.Guild.Id, set => set);
|
||||
roles = uow.SelfAssignedRoles.GetFromGuild(Context.Guild.Id);
|
||||
}
|
||||
SelfAssignedRole roleModel;
|
||||
if ((roleModel = roles.FirstOrDefault(r=>r.RoleId == role.Id)) == null)
|
||||
if (roles.FirstOrDefault(r=>r.RoleId == role.Id) == null)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("That role is not self-assignable.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("self_assign_not").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
if (guildUser.RoleIds.Contains(role.Id))
|
||||
{
|
||||
await Context.Channel.SendErrorAsync($"You already have **{role.Name}** role.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("self_assign_already", Format.Bold(role.Name)).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (conf.ExclusiveSelfAssignedRoles)
|
||||
{
|
||||
var sameRoleId = guildUser.RoleIds.Where(r => roles.Select(sar => sar.RoleId).Contains(r)).FirstOrDefault();
|
||||
var sameRoleId = guildUser.RoleIds.FirstOrDefault(r => roles.Select(sar => sar.RoleId).Contains(r));
|
||||
var sameRole = Context.Guild.GetRole(sameRoleId);
|
||||
if (sameRoleId != default(ulong))
|
||||
{
|
||||
await Context.Channel.SendErrorAsync($"You already have **{sameRole?.Name}** `exclusive self-assigned` role.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("self_assign_already_excl", Format.Bold(sameRole?.Name)).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -184,11 +183,11 @@ namespace NadekoBot.Modules.Administration
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync($"⚠️ I am unable to add that role to you. `I can't add roles to owners or other roles higher than my role in the role hierarchy.`").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("self_assign_perms").ConfigureAwait(false);
|
||||
Console.WriteLine(ex);
|
||||
return;
|
||||
}
|
||||
var msg = await Context.Channel.SendConfirmAsync($"🆗 You now have **{role.Name}** role.").ConfigureAwait(false);
|
||||
var msg = await ReplyConfirmLocalized("self_assign_success",Format.Bold(role.Name)).ConfigureAwait(false);
|
||||
|
||||
if (conf.AutoDeleteSelfAssignedRoleMessages)
|
||||
{
|
||||
@ -210,15 +209,14 @@ namespace NadekoBot.Modules.Administration
|
||||
autoDeleteSelfAssignedRoleMessages = uow.GuildConfigs.For(Context.Guild.Id, set => set).AutoDeleteSelfAssignedRoleMessages;
|
||||
roles = uow.SelfAssignedRoles.GetFromGuild(Context.Guild.Id);
|
||||
}
|
||||
SelfAssignedRole roleModel;
|
||||
if ((roleModel = roles.FirstOrDefault(r => r.RoleId == role.Id)) == null)
|
||||
if (roles.FirstOrDefault(r => r.RoleId == role.Id) == null)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("💢 That role is not self-assignable.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("self_assign_not").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
if (!guildUser.RoleIds.Contains(role.Id))
|
||||
{
|
||||
await Context.Channel.SendErrorAsync($"❎ You don't have **{role.Name}** role.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("self_assign_not_have",Format.Bold(role.Name)).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
try
|
||||
@ -227,10 +225,10 @@ namespace NadekoBot.Modules.Administration
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync($"⚠️ I am unable to add that role to you. `I can't remove roles to owners or other roles higher than my role in the role hierarchy.`").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("self_assign_perms").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
var msg = await Context.Channel.SendConfirmAsync($"🆗 You no longer have **{role.Name}** role.").ConfigureAwait(false);
|
||||
var msg = await ReplyConfirmLocalized("self_assign_remove", Format.Bold(role.Name)).ConfigureAwait(false);
|
||||
|
||||
if (autoDeleteSelfAssignedRoleMessages)
|
||||
{
|
||||
|
@ -3,18 +3,139 @@ using Discord.Commands;
|
||||
using NadekoBot.Attributes;
|
||||
using NadekoBot.Extensions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Discord.WebSocket;
|
||||
using NadekoBot.Services;
|
||||
|
||||
namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
public partial class Administration
|
||||
{
|
||||
[Group]
|
||||
class SelfCommands : ModuleBase
|
||||
public class SelfCommands : NadekoSubmodule
|
||||
{
|
||||
private static volatile bool _forwardDMs;
|
||||
private static volatile bool _forwardDMsToAllOwners;
|
||||
|
||||
private static readonly object _locker = new object();
|
||||
|
||||
static SelfCommands()
|
||||
{
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
{
|
||||
var config = uow.BotConfig.GetOrCreate();
|
||||
_forwardDMs = config.ForwardMessages;
|
||||
_forwardDMsToAllOwners = config.ForwardToAllOwners;
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[OwnerOnly]
|
||||
public async Task ForwardMessages()
|
||||
{
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
{
|
||||
var config = uow.BotConfig.GetOrCreate();
|
||||
lock (_locker)
|
||||
_forwardDMs = config.ForwardMessages = !config.ForwardMessages;
|
||||
uow.Complete();
|
||||
}
|
||||
if (_forwardDMs)
|
||||
await ReplyConfirmLocalized("fwdm_start").ConfigureAwait(false);
|
||||
else
|
||||
await ReplyConfirmLocalized("fwdm_stop").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[OwnerOnly]
|
||||
public async Task ForwardToAll()
|
||||
{
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
{
|
||||
var config = uow.BotConfig.GetOrCreate();
|
||||
lock (_locker)
|
||||
_forwardDMsToAllOwners = config.ForwardToAllOwners = !config.ForwardToAllOwners;
|
||||
uow.Complete();
|
||||
}
|
||||
if (_forwardDMsToAllOwners)
|
||||
await ReplyConfirmLocalized("fwall_start").ConfigureAwait(false);
|
||||
else
|
||||
await ReplyConfirmLocalized("fwall_stop").ConfigureAwait(false);
|
||||
|
||||
}
|
||||
|
||||
public static async Task HandleDmForwarding(SocketMessage msg, List<IDMChannel> ownerChannels)
|
||||
{
|
||||
if (_forwardDMs && ownerChannels.Any())
|
||||
{
|
||||
var title = GetTextStatic("dm_from",
|
||||
NadekoBot.Localization.DefaultCultureInfo,
|
||||
typeof(Administration).Name.ToLowerInvariant()) +
|
||||
$" [{msg.Author}]({msg.Author.Id})";
|
||||
|
||||
var attachamentsTxt = GetTextStatic("attachments",
|
||||
NadekoBot.Localization.DefaultCultureInfo,
|
||||
typeof(Administration).Name.ToLowerInvariant());
|
||||
|
||||
var toSend = msg.Content;
|
||||
|
||||
if (msg.Attachments.Count > 0)
|
||||
{
|
||||
toSend += $"\n\n{Format.Code(attachamentsTxt)}:\n" +
|
||||
string.Join("\n", msg.Attachments.Select(a => a.ProxyUrl));
|
||||
}
|
||||
|
||||
if (_forwardDMsToAllOwners)
|
||||
{
|
||||
await Task.WhenAll(ownerChannels.Where(ch => ch.Recipient.Id != msg.Author.Id)
|
||||
.Select(ch => ch.SendConfirmAsync(title, toSend))).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
var firstOwnerChannel = ownerChannels.First();
|
||||
if (firstOwnerChannel.Recipient.Id != msg.Author.Id)
|
||||
{
|
||||
try
|
||||
{
|
||||
await firstOwnerChannel.SendConfirmAsync(title, toSend).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[OwnerOnly]
|
||||
public async Task ConnectShard(int shardid)
|
||||
{
|
||||
var shard = NadekoBot.Client.GetShard(shardid);
|
||||
|
||||
if (shard == null)
|
||||
{
|
||||
await ReplyErrorLocalized("no_shard_id").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
await ReplyConfirmLocalized("shard_reconnecting", Format.Bold("#" + shardid)).ConfigureAwait(false);
|
||||
await shard.ConnectAsync().ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("shard_reconnected", Format.Bold("#" + shardid)).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.Warn(ex);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[OwnerOnly]
|
||||
public async Task Leave([Remainder] string guildStr)
|
||||
@ -25,18 +146,18 @@ namespace NadekoBot.Modules.Administration
|
||||
|
||||
if (server == null)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("⚠️ Cannot find that server").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("no_server").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
if (server.OwnerId != NadekoBot.Client.CurrentUser.Id)
|
||||
{
|
||||
await server.LeaveAsync().ConfigureAwait(false);
|
||||
await Context.Channel.SendConfirmAsync("✅ Left server " + server.Name).ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("left_server", Format.Bold(server.Name)).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await server.DeleteAsync().ConfigureAwait(false);
|
||||
await Context.Channel.SendConfirmAsync("Deleted server " + server.Name).ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("deleted_server",Format.Bold(server.Name)).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,7 +166,14 @@ namespace NadekoBot.Modules.Administration
|
||||
[OwnerOnly]
|
||||
public async Task Die()
|
||||
{
|
||||
try { await Context.Channel.SendConfirmAsync("ℹ️ **Shutting down.**").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
|
||||
try
|
||||
{
|
||||
await ReplyConfirmLocalized("shutting_down").ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
await Task.Delay(2000).ConfigureAwait(false);
|
||||
Environment.Exit(0);
|
||||
}
|
||||
@ -59,7 +187,7 @@ namespace NadekoBot.Modules.Administration
|
||||
|
||||
await NadekoBot.Client.CurrentUser.ModifyAsync(u => u.Username = newName).ConfigureAwait(false);
|
||||
|
||||
await Context.Channel.SendConfirmAsync($"Bot name changed to **{newName}**").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("bot_name", Format.Bold(newName)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -68,7 +196,7 @@ namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
await NadekoBot.Client.SetStatusAsync(SettableUserStatusToUserStatus(status)).ConfigureAwait(false);
|
||||
|
||||
await Context.Channel.SendConfirmAsync($"Bot status changed to **{status}**").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("bot_status", Format.Bold(status.ToString())).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -90,7 +218,7 @@ namespace NadekoBot.Modules.Administration
|
||||
}
|
||||
}
|
||||
|
||||
await Context.Channel.SendConfirmAsync("🆒 **New avatar set.**").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("set_avatar").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -99,7 +227,7 @@ namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
await NadekoBot.Client.SetGameAsync(game).ConfigureAwait(false);
|
||||
|
||||
await Context.Channel.SendConfirmAsync("👾 **New game set.**").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("set_game").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -110,7 +238,7 @@ namespace NadekoBot.Modules.Administration
|
||||
|
||||
await NadekoBot.Client.SetGameAsync(name, url, StreamType.Twitch).ConfigureAwait(false);
|
||||
|
||||
await Context.Channel.SendConfirmAsync("ℹ️ **New stream set.**").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("set_stream").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -124,7 +252,7 @@ namespace NadekoBot.Modules.Administration
|
||||
if (ids.Length != 2)
|
||||
return;
|
||||
var sid = ulong.Parse(ids[0]);
|
||||
var server = NadekoBot.Client.GetGuilds().Where(s => s.Id == sid).FirstOrDefault();
|
||||
var server = NadekoBot.Client.GetGuilds().FirstOrDefault(s => s.Id == sid);
|
||||
|
||||
if (server == null)
|
||||
return;
|
||||
@ -132,7 +260,7 @@ namespace NadekoBot.Modules.Administration
|
||||
if (ids[1].ToUpperInvariant().StartsWith("C:"))
|
||||
{
|
||||
var cid = ulong.Parse(ids[1].Substring(2));
|
||||
var ch = server.TextChannels.Where(c => c.Id == cid).FirstOrDefault();
|
||||
var ch = server.TextChannels.FirstOrDefault(c => c.Id == cid);
|
||||
if (ch == null)
|
||||
{
|
||||
return;
|
||||
@ -142,7 +270,7 @@ namespace NadekoBot.Modules.Administration
|
||||
else if (ids[1].ToUpperInvariant().StartsWith("U:"))
|
||||
{
|
||||
var uid = ulong.Parse(ids[1].Substring(2));
|
||||
var user = server.Users.Where(u => u.Id == uid).FirstOrDefault();
|
||||
var user = server.Users.FirstOrDefault(u => u.Id == uid);
|
||||
if (user == null)
|
||||
{
|
||||
return;
|
||||
@ -151,8 +279,10 @@ namespace NadekoBot.Modules.Administration
|
||||
}
|
||||
else
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("⚠️ Invalid format.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("invalid_format").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
await ReplyConfirmLocalized("message_sent").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -162,10 +292,18 @@ namespace NadekoBot.Modules.Administration
|
||||
var channels = NadekoBot.Client.GetGuilds().Select(g => g.DefaultChannel).ToArray();
|
||||
if (channels == null)
|
||||
return;
|
||||
await Task.WhenAll(channels.Where(c => c != null).Select(c => c.SendConfirmAsync($"🆕 Message from {Context.User} `[Bot Owner]`:", message)))
|
||||
await Task.WhenAll(channels.Where(c => c != null).Select(c => c.SendConfirmAsync(GetText("message_from_bo", Context.User.ToString()), message)))
|
||||
.ConfigureAwait(false);
|
||||
|
||||
await Context.Channel.SendConfirmAsync("🆗").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("message_sent").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[OwnerOnly]
|
||||
public async Task ReloadImages()
|
||||
{
|
||||
var time = await NadekoBot.Images.Reload().ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("images_loaded", time.TotalSeconds.ToString("F3")).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private static UserStatus SettableUserStatusToUserStatus(SettableUserStatus sus)
|
||||
@ -184,6 +322,14 @@ namespace NadekoBot.Modules.Administration
|
||||
|
||||
return UserStatus.Online;
|
||||
}
|
||||
|
||||
public enum SettableUserStatus
|
||||
{
|
||||
Online,
|
||||
Invisible,
|
||||
Idle,
|
||||
Dnd
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,9 @@
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using Discord.WebSocket;
|
||||
using NadekoBot.Attributes;
|
||||
using NadekoBot.DataStructures;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Services;
|
||||
using NadekoBot.Services.Database;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using NLog;
|
||||
using System;
|
||||
@ -17,10 +16,10 @@ namespace NadekoBot.Modules.Administration
|
||||
public partial class Administration
|
||||
{
|
||||
[Group]
|
||||
public class ServerGreetCommands : ModuleBase
|
||||
public class ServerGreetCommands : NadekoSubmodule
|
||||
{
|
||||
//make this to a field in the guildconfig table
|
||||
class GreetSettings
|
||||
private class GreetSettings
|
||||
{
|
||||
public int AutoDeleteGreetMessagesTimer { get; set; }
|
||||
public int AutoDeleteByeMessagesTimer { get; set; }
|
||||
@ -52,9 +51,9 @@ namespace NadekoBot.Modules.Administration
|
||||
};
|
||||
}
|
||||
|
||||
private static Logger _log { get; }
|
||||
private new static Logger _log { get; }
|
||||
|
||||
private static ConcurrentDictionary<ulong, GreetSettings> GuildConfigsCache { get; } = new ConcurrentDictionary<ulong, GreetSettings>();
|
||||
private static ConcurrentDictionary<ulong, GreetSettings> guildConfigsCache { get; }
|
||||
|
||||
static ServerGreetCommands()
|
||||
{
|
||||
@ -62,13 +61,13 @@ namespace NadekoBot.Modules.Administration
|
||||
NadekoBot.Client.UserLeft += UserLeft;
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
GuildConfigsCache = new ConcurrentDictionary<ulong, GreetSettings>(NadekoBot.AllGuildConfigs.ToDictionary(g => g.GuildId, (g) => GreetSettings.Create(g)));
|
||||
guildConfigsCache = new ConcurrentDictionary<ulong, GreetSettings>(NadekoBot.AllGuildConfigs.ToDictionary(g => g.GuildId, GreetSettings.Create));
|
||||
}
|
||||
|
||||
private static GreetSettings GetOrAddSettingsForGuild(ulong guildId)
|
||||
{
|
||||
GreetSettings settings;
|
||||
GuildConfigsCache.TryGetValue(guildId, out settings);
|
||||
guildConfigsCache.TryGetValue(guildId, out settings);
|
||||
|
||||
if (settings != null)
|
||||
return settings;
|
||||
@ -79,81 +78,147 @@ namespace NadekoBot.Modules.Administration
|
||||
settings = GreetSettings.Create(gc);
|
||||
}
|
||||
|
||||
GuildConfigsCache.TryAdd(guildId, settings);
|
||||
guildConfigsCache.TryAdd(guildId, settings);
|
||||
return settings;
|
||||
}
|
||||
|
||||
//todo optimize ASAP
|
||||
private static async Task UserLeft(IGuildUser user)
|
||||
private static Task UserLeft(IGuildUser user)
|
||||
{
|
||||
try
|
||||
var _ = Task.Run(async () =>
|
||||
{
|
||||
var conf = GetOrAddSettingsForGuild(user.GuildId);
|
||||
|
||||
if (!conf.SendChannelByeMessage) return;
|
||||
var channel = (await user.Guild.GetTextChannelsAsync()).SingleOrDefault(c => c.Id == conf.ByeMessageChannelId);
|
||||
|
||||
if (channel == null) //maybe warn the server owner that the channel is missing
|
||||
return;
|
||||
|
||||
var msg = conf.ChannelByeMessageText.Replace("%user%", user.Username).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
|
||||
if (string.IsNullOrWhiteSpace(msg))
|
||||
return;
|
||||
try
|
||||
{
|
||||
var toDelete = await channel.SendMessageAsync(msg.SanitizeMentions()).ConfigureAwait(false);
|
||||
if (conf.AutoDeleteByeMessagesTimer > 0)
|
||||
var conf = GetOrAddSettingsForGuild(user.GuildId);
|
||||
|
||||
if (!conf.SendChannelByeMessage) return;
|
||||
var channel = (await user.Guild.GetTextChannelsAsync()).SingleOrDefault(c => c.Id == conf.ByeMessageChannelId);
|
||||
|
||||
if (channel == null) //maybe warn the server owner that the channel is missing
|
||||
return;
|
||||
CREmbed embedData;
|
||||
if (CREmbed.TryParse(conf.ChannelByeMessageText, out embedData))
|
||||
{
|
||||
toDelete.DeleteAfter(conf.AutoDeleteByeMessagesTimer);
|
||||
embedData.PlainText = embedData.PlainText?.Replace("%user%", user.Username).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
|
||||
embedData.Description = embedData.Description?.Replace("%user%", user.Username).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
|
||||
embedData.Title = embedData.Title?.Replace("%user%", user.Username).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
|
||||
try
|
||||
{
|
||||
var toDelete = await channel.EmbedAsync(embedData.ToEmbed(), embedData.PlainText ?? "").ConfigureAwait(false);
|
||||
if (conf.AutoDeleteByeMessagesTimer > 0)
|
||||
{
|
||||
toDelete.DeleteAfter(conf.AutoDeleteByeMessagesTimer);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) { _log.Warn(ex); }
|
||||
}
|
||||
else
|
||||
{
|
||||
var msg = conf.ChannelByeMessageText.Replace("%user%", user.Username).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
|
||||
if (string.IsNullOrWhiteSpace(msg))
|
||||
return;
|
||||
try
|
||||
{
|
||||
var toDelete = await channel.SendMessageAsync(msg.SanitizeMentions()).ConfigureAwait(false);
|
||||
if (conf.AutoDeleteByeMessagesTimer > 0)
|
||||
{
|
||||
toDelete.DeleteAfter(conf.AutoDeleteByeMessagesTimer);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) { _log.Warn(ex); }
|
||||
}
|
||||
}
|
||||
catch (Exception ex) { _log.Warn(ex); }
|
||||
}
|
||||
catch { }
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
});
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private static async Task UserJoined(IGuildUser user)
|
||||
private static Task UserJoined(IGuildUser user)
|
||||
{
|
||||
try
|
||||
var _ = Task.Run(async () =>
|
||||
{
|
||||
var conf = GetOrAddSettingsForGuild(user.GuildId);
|
||||
|
||||
if (conf.SendChannelGreetMessage)
|
||||
try
|
||||
{
|
||||
var channel = (await user.Guild.GetTextChannelsAsync()).SingleOrDefault(c => c.Id == conf.GreetMessageChannelId);
|
||||
if (channel != null) //maybe warn the server owner that the channel is missing
|
||||
var conf = GetOrAddSettingsForGuild(user.GuildId);
|
||||
|
||||
if (conf.SendChannelGreetMessage)
|
||||
{
|
||||
var msg = conf.ChannelGreetMessageText.Replace("%user%", user.Mention).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
|
||||
if (!string.IsNullOrWhiteSpace(msg))
|
||||
var channel = (await user.Guild.GetTextChannelsAsync()).SingleOrDefault(c => c.Id == conf.GreetMessageChannelId);
|
||||
if (channel != null) //maybe warn the server owner that the channel is missing
|
||||
{
|
||||
try
|
||||
|
||||
CREmbed embedData;
|
||||
if (CREmbed.TryParse(conf.ChannelGreetMessageText, out embedData))
|
||||
{
|
||||
var toDelete = await channel.SendMessageAsync(msg.SanitizeMentions()).ConfigureAwait(false);
|
||||
if (conf.AutoDeleteGreetMessagesTimer > 0)
|
||||
embedData.PlainText = embedData.PlainText?.Replace("%user%", user.Mention).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
|
||||
embedData.Description = embedData.Description?.Replace("%user%", user.Mention).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
|
||||
embedData.Title = embedData.Title?.Replace("%user%", user.ToString()).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
|
||||
try
|
||||
{
|
||||
toDelete.DeleteAfter(conf.AutoDeleteGreetMessagesTimer);
|
||||
var toDelete = await channel.EmbedAsync(embedData.ToEmbed(), embedData.PlainText ?? "").ConfigureAwait(false);
|
||||
if (conf.AutoDeleteGreetMessagesTimer > 0)
|
||||
{
|
||||
toDelete.DeleteAfter(conf.AutoDeleteGreetMessagesTimer);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) { _log.Warn(ex); }
|
||||
}
|
||||
else
|
||||
{
|
||||
var msg = conf.ChannelGreetMessageText.Replace("%user%", user.Mention).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
|
||||
if (!string.IsNullOrWhiteSpace(msg))
|
||||
{
|
||||
try
|
||||
{
|
||||
var toDelete = await channel.SendMessageAsync(msg.SanitizeMentions()).ConfigureAwait(false);
|
||||
if (conf.AutoDeleteGreetMessagesTimer > 0)
|
||||
{
|
||||
toDelete.DeleteAfter(conf.AutoDeleteGreetMessagesTimer);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) { _log.Warn(ex); }
|
||||
}
|
||||
}
|
||||
catch (Exception ex) { _log.Warn(ex); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (conf.SendDmGreetMessage)
|
||||
{
|
||||
var channel = await user.CreateDMChannelAsync();
|
||||
|
||||
if (channel != null)
|
||||
if (conf.SendDmGreetMessage)
|
||||
{
|
||||
var msg = conf.DmGreetMessageText.Replace("%user%", user.Username).Replace("%server%", user.Guild.Name);
|
||||
if (!string.IsNullOrWhiteSpace(msg))
|
||||
var channel = await user.CreateDMChannelAsync();
|
||||
|
||||
if (channel != null)
|
||||
{
|
||||
await channel.SendConfirmAsync(msg).ConfigureAwait(false);
|
||||
CREmbed embedData;
|
||||
if (CREmbed.TryParse(conf.ChannelGreetMessageText, out embedData))
|
||||
{
|
||||
embedData.PlainText = embedData.PlainText?.Replace("%user%", user.ToString()).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
|
||||
embedData.Description = embedData.Description?.Replace("%user%", user.ToString()).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
|
||||
embedData.Title = embedData.Title?.Replace("%user%", user.ToString()).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
|
||||
try
|
||||
{
|
||||
await channel.EmbedAsync(embedData.ToEmbed(), embedData.PlainText ?? "").ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex) { _log.Warn(ex); }
|
||||
}
|
||||
else
|
||||
{
|
||||
var msg = conf.DmGreetMessageText.Replace("%user%", user.ToString()).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
|
||||
if (!string.IsNullOrWhiteSpace(msg))
|
||||
{
|
||||
await channel.SendConfirmAsync(msg).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
});
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -161,16 +226,15 @@ namespace NadekoBot.Modules.Administration
|
||||
[RequireUserPermission(GuildPermission.ManageGuild)]
|
||||
public async Task GreetDel(int timer = 30)
|
||||
{
|
||||
var channel = (ITextChannel)Context.Channel;
|
||||
if (timer < 0 || timer > 600)
|
||||
return;
|
||||
|
||||
await ServerGreetCommands.SetGreetDel(Context.Guild.Id, timer).ConfigureAwait(false);
|
||||
|
||||
if (timer > 0)
|
||||
await Context.Channel.SendConfirmAsync($"🆗 Greet messages **will be deleted** after `{timer} seconds`.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("greetdel_on", timer).ConfigureAwait(false);
|
||||
else
|
||||
await Context.Channel.SendConfirmAsync("ℹ️ Automatic deletion of greet messages has been **disabled**.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("greetdel_off").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private static async Task SetGreetDel(ulong id, int timer)
|
||||
@ -184,7 +248,7 @@ namespace NadekoBot.Modules.Administration
|
||||
conf.AutoDeleteGreetMessagesTimer = timer;
|
||||
|
||||
var toAdd = GreetSettings.Create(conf);
|
||||
GuildConfigsCache.AddOrUpdate(id, toAdd, (key, old) => toAdd);
|
||||
guildConfigsCache.AddOrUpdate(id, toAdd, (key, old) => toAdd);
|
||||
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
@ -198,9 +262,9 @@ namespace NadekoBot.Modules.Administration
|
||||
var enabled = await ServerGreetCommands.SetGreet(Context.Guild.Id, Context.Channel.Id).ConfigureAwait(false);
|
||||
|
||||
if (enabled)
|
||||
await Context.Channel.SendConfirmAsync("✅ Greeting messages **enabled** on this channel.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("greet_on").ConfigureAwait(false);
|
||||
else
|
||||
await Context.Channel.SendConfirmAsync("ℹ️ Greeting messages **disabled**.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("greet_off").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private static async Task<bool> SetGreet(ulong guildId, ulong channelId, bool? value = null)
|
||||
@ -213,7 +277,7 @@ namespace NadekoBot.Modules.Administration
|
||||
conf.GreetMessageChannelId = channelId;
|
||||
|
||||
var toAdd = GreetSettings.Create(conf);
|
||||
GuildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd);
|
||||
guildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd);
|
||||
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
@ -232,15 +296,15 @@ namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
channelGreetMessageText = uow.GuildConfigs.For(Context.Guild.Id, set => set).ChannelGreetMessageText;
|
||||
}
|
||||
await Context.Channel.SendConfirmAsync("Current greet message: ", channelGreetMessageText?.SanitizeMentions());
|
||||
await ReplyConfirmLocalized("greetmsg_cur", channelGreetMessageText?.SanitizeMentions()).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
var sendGreetEnabled = ServerGreetCommands.SetGreetMessage(Context.Guild.Id, ref text);
|
||||
|
||||
await Context.Channel.SendConfirmAsync("🆗 New greet message **set**.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("greetmsg_new").ConfigureAwait(false);
|
||||
if (!sendGreetEnabled)
|
||||
await Context.Channel.SendConfirmAsync("ℹ️ Enable greet messsages by typing `.greet`").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("greetmsg_enable", $"`{Prefix}greet`").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public static bool SetGreetMessage(ulong guildId, ref string message)
|
||||
@ -258,7 +322,7 @@ namespace NadekoBot.Modules.Administration
|
||||
greetMsgEnabled = conf.SendChannelGreetMessage;
|
||||
|
||||
var toAdd = GreetSettings.Create(conf);
|
||||
GuildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd);
|
||||
guildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd);
|
||||
|
||||
uow.Complete();
|
||||
}
|
||||
@ -273,9 +337,9 @@ namespace NadekoBot.Modules.Administration
|
||||
var enabled = await ServerGreetCommands.SetGreetDm(Context.Guild.Id).ConfigureAwait(false);
|
||||
|
||||
if (enabled)
|
||||
await Context.Channel.SendConfirmAsync("🆗 DM Greet announcements **enabled**.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("greetdm_on").ConfigureAwait(false);
|
||||
else
|
||||
await Context.Channel.SendConfirmAsync("ℹ️ Greet announcements **disabled**.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("greetdm_off").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private static async Task<bool> SetGreetDm(ulong guildId, bool? value = null)
|
||||
@ -287,7 +351,7 @@ namespace NadekoBot.Modules.Administration
|
||||
enabled = conf.SendDmGreetMessage = value ?? !conf.SendDmGreetMessage;
|
||||
|
||||
var toAdd = GreetSettings.Create(conf);
|
||||
GuildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd);
|
||||
guildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd);
|
||||
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
@ -306,15 +370,15 @@ namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
config = uow.GuildConfigs.For(Context.Guild.Id);
|
||||
}
|
||||
await Context.Channel.SendConfirmAsync("ℹ️ Current **DM greet** message: `" + config.DmGreetMessageText?.SanitizeMentions() + "`");
|
||||
await ReplyConfirmLocalized("greetdmmsg_cur", config.DmGreetMessageText?.SanitizeMentions()).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
var sendGreetEnabled = ServerGreetCommands.SetGreetDmMessage(Context.Guild.Id, ref text);
|
||||
|
||||
await Context.Channel.SendConfirmAsync("🆗 New DM greet message **set**.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("greetdmmsg_new").ConfigureAwait(false);
|
||||
if (!sendGreetEnabled)
|
||||
await Context.Channel.SendConfirmAsync($"ℹ️ Enable DM greet messsages by typing `{NadekoBot.ModulePrefixes[typeof(Administration).Name]}greetdm`").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("greetdmmsg_enable", $"`{Prefix}greetdm`").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public static bool SetGreetDmMessage(ulong guildId, ref string message)
|
||||
@ -332,7 +396,7 @@ namespace NadekoBot.Modules.Administration
|
||||
greetMsgEnabled = conf.SendDmGreetMessage;
|
||||
|
||||
var toAdd = GreetSettings.Create(conf);
|
||||
GuildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd);
|
||||
guildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd);
|
||||
|
||||
uow.Complete();
|
||||
}
|
||||
@ -347,9 +411,9 @@ namespace NadekoBot.Modules.Administration
|
||||
var enabled = await ServerGreetCommands.SetBye(Context.Guild.Id, Context.Channel.Id).ConfigureAwait(false);
|
||||
|
||||
if (enabled)
|
||||
await Context.Channel.SendConfirmAsync("✅ Bye announcements **enabled** on this channel.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("bye_on").ConfigureAwait(false);
|
||||
else
|
||||
await Context.Channel.SendConfirmAsync("ℹ️ Bye announcements **disabled**.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("bye_off").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private static async Task<bool> SetBye(ulong guildId, ulong channelId, bool? value = null)
|
||||
@ -362,7 +426,7 @@ namespace NadekoBot.Modules.Administration
|
||||
conf.ByeMessageChannelId = channelId;
|
||||
|
||||
var toAdd = GreetSettings.Create(conf);
|
||||
GuildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd);
|
||||
guildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd);
|
||||
|
||||
await uow.CompleteAsync();
|
||||
}
|
||||
@ -381,15 +445,15 @@ namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
byeMessageText = uow.GuildConfigs.For(Context.Guild.Id, set => set).ChannelByeMessageText;
|
||||
}
|
||||
await Context.Channel.SendConfirmAsync("ℹ️ Current **bye** message: `" + byeMessageText?.SanitizeMentions() + "`");
|
||||
await ReplyConfirmLocalized("byemsg_cur", byeMessageText?.SanitizeMentions()).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
var sendByeEnabled = ServerGreetCommands.SetByeMessage(Context.Guild.Id, ref text);
|
||||
|
||||
await Context.Channel.SendConfirmAsync("🆗 New bye message **set**.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("byemsg_new").ConfigureAwait(false);
|
||||
if (!sendByeEnabled)
|
||||
await Context.Channel.SendConfirmAsync($"ℹ️ Enable bye messsages by typing `{NadekoBot.ModulePrefixes[typeof(Administration).Name]}bye`").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("byemsg_enable", $"`{Prefix}bye`").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public static bool SetByeMessage(ulong guildId, ref string message)
|
||||
@ -407,7 +471,7 @@ namespace NadekoBot.Modules.Administration
|
||||
byeMsgEnabled = conf.SendChannelByeMessage;
|
||||
|
||||
var toAdd = GreetSettings.Create(conf);
|
||||
GuildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd);
|
||||
guildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd);
|
||||
|
||||
uow.Complete();
|
||||
}
|
||||
@ -422,9 +486,9 @@ namespace NadekoBot.Modules.Administration
|
||||
await ServerGreetCommands.SetByeDel(Context.Guild.Id, timer).ConfigureAwait(false);
|
||||
|
||||
if (timer > 0)
|
||||
await Context.Channel.SendConfirmAsync($"🆗 Bye messages **will be deleted** after `{timer} seconds`.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("byedel_on", timer).ConfigureAwait(false);
|
||||
else
|
||||
await Context.Channel.SendConfirmAsync("ℹ️ Automatic deletion of bye messages has been **disabled**.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("byedel_off").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private static async Task SetByeDel(ulong guildId, int timer)
|
||||
@ -438,7 +502,7 @@ namespace NadekoBot.Modules.Administration
|
||||
conf.AutoDeleteByeMessagesTimer = timer;
|
||||
|
||||
var toAdd = GreetSettings.Create(conf);
|
||||
GuildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd);
|
||||
guildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd);
|
||||
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
@ -7,9 +7,11 @@ using NadekoBot.Services;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NadekoBot.Modules.Administration
|
||||
@ -17,96 +19,141 @@ namespace NadekoBot.Modules.Administration
|
||||
public partial class Administration
|
||||
{
|
||||
[Group]
|
||||
public class VoicePlusTextCommands : ModuleBase
|
||||
public class VoicePlusTextCommands : NadekoSubmodule
|
||||
{
|
||||
private static Regex channelNameRegex = new Regex(@"[^a-zA-Z0-9 -]", RegexOptions.Compiled);
|
||||
private new static readonly Logger _log;
|
||||
|
||||
private static ConcurrentHashSet<ulong> voicePlusTextCache { get; }
|
||||
private static readonly Regex _channelNameRegex = new Regex(@"[^a-zA-Z0-9 -]", RegexOptions.Compiled);
|
||||
|
||||
private static readonly ConcurrentHashSet<ulong> _voicePlusTextCache;
|
||||
private static readonly ConcurrentDictionary<ulong, SemaphoreSlim> _guildLockObjects = new ConcurrentDictionary<ulong, SemaphoreSlim>();
|
||||
static VoicePlusTextCommands()
|
||||
{
|
||||
var _log = LogManager.GetCurrentClassLogger();
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
var sw = Stopwatch.StartNew();
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
{
|
||||
voicePlusTextCache = new ConcurrentHashSet<ulong>(NadekoBot.AllGuildConfigs.Where(g => g.VoicePlusTextEnabled).Select(g => g.GuildId));
|
||||
}
|
||||
|
||||
_voicePlusTextCache = new ConcurrentHashSet<ulong>(NadekoBot.AllGuildConfigs.Where(g => g.VoicePlusTextEnabled).Select(g => g.GuildId));
|
||||
NadekoBot.Client.UserVoiceStateUpdated += UserUpdatedEventHandler;
|
||||
|
||||
sw.Stop();
|
||||
_log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s");
|
||||
}
|
||||
|
||||
private static async Task UserUpdatedEventHandler(SocketUser iuser, SocketVoiceState before, SocketVoiceState after)
|
||||
private static Task UserUpdatedEventHandler(SocketUser iuser, SocketVoiceState before, SocketVoiceState after)
|
||||
{
|
||||
var user = (iuser as SocketGuildUser);
|
||||
var guild = user?.Guild;
|
||||
|
||||
if (guild == null)
|
||||
return;
|
||||
return Task.CompletedTask;
|
||||
|
||||
try
|
||||
var botUserPerms = guild.CurrentUser.GuildPermissions;
|
||||
|
||||
if (before.VoiceChannel == after.VoiceChannel)
|
||||
return Task.CompletedTask;
|
||||
|
||||
if (!_voicePlusTextCache.Contains(guild.Id))
|
||||
return Task.CompletedTask;
|
||||
|
||||
var _ = Task.Run(async () =>
|
||||
{
|
||||
var botUserPerms = guild.CurrentUser.GuildPermissions;
|
||||
|
||||
if (before.VoiceChannel == after.VoiceChannel) return;
|
||||
|
||||
if (!voicePlusTextCache.Contains(guild.Id))
|
||||
return;
|
||||
|
||||
if (!botUserPerms.ManageChannels || !botUserPerms.ManageRoles)
|
||||
try
|
||||
{
|
||||
|
||||
if (!botUserPerms.ManageChannels || !botUserPerms.ManageRoles)
|
||||
{
|
||||
try
|
||||
{
|
||||
await guild.Owner.SendErrorAsync(
|
||||
GetTextStatic("vt_exit",
|
||||
NadekoBot.Localization.GetCultureInfo(guild),
|
||||
typeof(Administration).Name.ToLowerInvariant(),
|
||||
Format.Bold(guild.Name))).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
{
|
||||
uow.GuildConfigs.For(guild.Id, set => set).VoicePlusTextEnabled = false;
|
||||
_voicePlusTextCache.TryRemove(guild.Id);
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var semaphore = _guildLockObjects.GetOrAdd(guild.Id, (key) => new SemaphoreSlim(1, 1));
|
||||
|
||||
try
|
||||
{
|
||||
await guild.Owner.SendErrorAsync(
|
||||
"⚠️ I don't have **manage server** and/or **manage channels** permission," +
|
||||
$" so I cannot run `voice+text` on **{guild.Name}** server.").ConfigureAwait(false);
|
||||
}
|
||||
catch { }
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
{
|
||||
uow.GuildConfigs.For(guild.Id, set => set).VoicePlusTextEnabled = false;
|
||||
voicePlusTextCache.TryRemove(guild.Id);
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
await semaphore.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
var beforeVch = before.VoiceChannel;
|
||||
if (beforeVch != null)
|
||||
{
|
||||
var beforeRoleName = GetRoleName(beforeVch);
|
||||
var beforeRole = guild.Roles.FirstOrDefault(x => x.Name == beforeRoleName);
|
||||
if (beforeRole != null)
|
||||
try
|
||||
{
|
||||
_log.Info("Removing role " + beforeRoleName + " from user " + user.Username);
|
||||
await user.RemoveRolesAsync(beforeRole).ConfigureAwait(false);
|
||||
await Task.Delay(200).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.Warn(ex);
|
||||
}
|
||||
}
|
||||
var afterVch = after.VoiceChannel;
|
||||
if (afterVch != null && guild.AFKChannel?.Id != afterVch.Id)
|
||||
{
|
||||
var roleName = GetRoleName(afterVch);
|
||||
var roleToAdd = guild.Roles.FirstOrDefault(x => x.Name == roleName) ??
|
||||
(IRole) await guild.CreateRoleAsync(roleName, GuildPermissions.None).ConfigureAwait(false);
|
||||
|
||||
var beforeVch = before.VoiceChannel;
|
||||
if (beforeVch != null)
|
||||
{
|
||||
var textChannel = guild.TextChannels.Where(t => t.Name == GetChannelName(beforeVch.Name).ToLowerInvariant()).FirstOrDefault();
|
||||
if (textChannel != null)
|
||||
await textChannel.AddPermissionOverwriteAsync(user,
|
||||
new OverwritePermissions(readMessages: PermValue.Deny,
|
||||
sendMessages: PermValue.Deny)).ConfigureAwait(false);
|
||||
}
|
||||
var afterVch = after.VoiceChannel;
|
||||
if (afterVch != null && guild.AFKChannel?.Id != afterVch.Id)
|
||||
{
|
||||
ITextChannel textChannel = guild.TextChannels
|
||||
.Where(t => t.Name == GetChannelName(afterVch.Name).ToLowerInvariant())
|
||||
.FirstOrDefault();
|
||||
if (textChannel == null)
|
||||
{
|
||||
textChannel = (await guild.CreateTextChannelAsync(GetChannelName(afterVch.Name).ToLowerInvariant()).ConfigureAwait(false));
|
||||
await textChannel.AddPermissionOverwriteAsync(guild.EveryoneRole,
|
||||
new OverwritePermissions(readMessages: PermValue.Deny,
|
||||
sendMessages: PermValue.Deny)).ConfigureAwait(false);
|
||||
ITextChannel textChannel = guild.TextChannels
|
||||
.FirstOrDefault(t => t.Name == GetChannelName(afterVch.Name).ToLowerInvariant());
|
||||
if (textChannel == null)
|
||||
{
|
||||
var created = (await guild.CreateTextChannelAsync(GetChannelName(afterVch.Name).ToLowerInvariant()).ConfigureAwait(false));
|
||||
|
||||
try { await guild.CurrentUser.AddRolesAsync(roleToAdd).ConfigureAwait(false); } catch {/*ignored*/}
|
||||
await Task.Delay(50).ConfigureAwait(false);
|
||||
await created.AddPermissionOverwriteAsync(roleToAdd, new OverwritePermissions(
|
||||
readMessages: PermValue.Allow,
|
||||
sendMessages: PermValue.Allow))
|
||||
.ConfigureAwait(false);
|
||||
await Task.Delay(50).ConfigureAwait(false);
|
||||
await created.AddPermissionOverwriteAsync(guild.EveryoneRole, new OverwritePermissions(
|
||||
readMessages: PermValue.Deny,
|
||||
sendMessages: PermValue.Deny))
|
||||
.ConfigureAwait(false);
|
||||
await Task.Delay(50).ConfigureAwait(false);
|
||||
}
|
||||
_log.Warn("Adding role " + roleToAdd.Name + " to user " + user.Username);
|
||||
await user.AddRolesAsync(roleToAdd).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
semaphore.Release();
|
||||
}
|
||||
await textChannel.AddPermissionOverwriteAsync(user,
|
||||
new OverwritePermissions(readMessages: PermValue.Allow,
|
||||
sendMessages: PermValue.Allow)).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.Warn(ex);
|
||||
}
|
||||
});
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private static string GetChannelName(string voiceName) =>
|
||||
channelNameRegex.Replace(voiceName, "").Trim().Replace(" ", "-").TrimTo(90, true) + "-voice";
|
||||
_channelNameRegex.Replace(voiceName, "").Trim().Replace(" ", "-").TrimTo(90, true) + "-voice";
|
||||
|
||||
private static string GetRoleName(IVoiceChannel ch) =>
|
||||
"nvoice-" + ch.Id;
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
@ -119,7 +166,7 @@ namespace NadekoBot.Modules.Administration
|
||||
var botUser = await guild.GetCurrentUserAsync().ConfigureAwait(false);
|
||||
if (!botUser.GuildPermissions.ManageRoles || !botUser.GuildPermissions.ManageChannels)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("I require atleast **manage roles** and **manage channels permissions** to enable this feature. `(preffered Administration permission)`");
|
||||
await ReplyErrorLocalized("vt_no_perms").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -127,10 +174,12 @@ namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
try
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("⚠️ You are enabling this feature and **I do not have ADMINISTRATOR permissions**. " +
|
||||
"`This may cause some issues, and you will have to clean up text channels yourself afterwards.`");
|
||||
await ReplyErrorLocalized("vt_no_admin").ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
try
|
||||
{
|
||||
@ -143,16 +192,23 @@ namespace NadekoBot.Modules.Administration
|
||||
}
|
||||
if (!isEnabled)
|
||||
{
|
||||
voicePlusTextCache.TryRemove(guild.Id);
|
||||
_voicePlusTextCache.TryRemove(guild.Id);
|
||||
foreach (var textChannel in (await guild.GetTextChannelsAsync().ConfigureAwait(false)).Where(c => c.Name.EndsWith("-voice")))
|
||||
{
|
||||
try { await textChannel.DeleteAsync().ConfigureAwait(false); } catch { }
|
||||
await Task.Delay(500).ConfigureAwait(false);
|
||||
}
|
||||
await Context.Channel.SendConfirmAsync("ℹ️ Successfuly **removed** voice + text feature.").ConfigureAwait(false);
|
||||
|
||||
foreach (var role in guild.Roles.Where(c => c.Name.StartsWith("nvoice-")))
|
||||
{
|
||||
try { await role.DeleteAsync().ConfigureAwait(false); } catch { }
|
||||
await Task.Delay(500).ConfigureAwait(false);
|
||||
}
|
||||
await ReplyConfirmLocalized("vt_disabled").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
voicePlusTextCache.Add(guild.Id);
|
||||
await Context.Channel.SendConfirmAsync("🆗 Successfuly **enabled** voice + text feature.").ConfigureAwait(false);
|
||||
_voicePlusTextCache.Add(guild.Id);
|
||||
await ReplyConfirmLocalized("vt_enabled").ConfigureAwait(false);
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -163,29 +219,43 @@ namespace NadekoBot.Modules.Administration
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[RequireUserPermission(GuildPermission.ManageChannels)]
|
||||
[RequireBotPermission(GuildPermission.ManageChannels)]
|
||||
[RequireUserPermission(GuildPermission.ManageRoles)]
|
||||
//[RequireBotPermission(GuildPermission.ManageRoles)]
|
||||
public async Task CleanVPlusT()
|
||||
{
|
||||
var guild = Context.Guild;
|
||||
var botUser = await guild.GetCurrentUserAsync().ConfigureAwait(false);
|
||||
if (!botUser.GuildPermissions.Administrator)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("I need **Administrator permission** to do that.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("need_admin").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
var allTxtChannels = (await guild.GetTextChannelsAsync()).Where(c => c.Name.EndsWith("-voice"));
|
||||
var validTxtChannelNames = (await guild.GetVoiceChannelsAsync()).Select(c => GetChannelName(c.Name).ToLowerInvariant());
|
||||
var textChannels = await guild.GetTextChannelsAsync().ConfigureAwait(false);
|
||||
var voiceChannels = await guild.GetVoiceChannelsAsync().ConfigureAwait(false);
|
||||
|
||||
var invalidTxtChannels = allTxtChannels.Where(c => !validTxtChannelNames.Contains(c.Name));
|
||||
var boundTextChannels = textChannels.Where(c => c.Name.EndsWith("-voice"));
|
||||
var validTxtChannelNames = new HashSet<string>(voiceChannels.Select(c => GetChannelName(c.Name).ToLowerInvariant()));
|
||||
var invalidTxtChannels = boundTextChannels.Where(c => !validTxtChannelNames.Contains(c.Name));
|
||||
|
||||
foreach (var c in invalidTxtChannels)
|
||||
{
|
||||
try { await c.DeleteAsync().ConfigureAwait(false); } catch { }
|
||||
await Task.Delay(500);
|
||||
await Task.Delay(500).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
var boundRoles = guild.Roles.Where(r => r.Name.StartsWith("nvoice-"));
|
||||
var validRoleNames = new HashSet<string>(voiceChannels.Select(c => GetRoleName(c).ToLowerInvariant()));
|
||||
var invalidRoles = boundRoles.Where(r => !validRoleNames.Contains(r.Name));
|
||||
|
||||
foreach (var r in invalidRoles)
|
||||
{
|
||||
try { await r.DeleteAsync().ConfigureAwait(false); } catch { }
|
||||
await Task.Delay(500).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await Context.Channel.SendConfirmAsync("Cleaned v+t.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("cleaned_up").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,24 +11,18 @@ using NadekoBot.Services.Database.Models;
|
||||
using System.Linq;
|
||||
using NadekoBot.Extensions;
|
||||
using System.Threading;
|
||||
using System.Diagnostics;
|
||||
using NLog;
|
||||
|
||||
namespace NadekoBot.Modules.ClashOfClans
|
||||
{
|
||||
[NadekoModule("ClashOfClans", ",")]
|
||||
public class ClashOfClans : DiscordModule
|
||||
public class ClashOfClans : NadekoTopLevelModule
|
||||
{
|
||||
public static ConcurrentDictionary<ulong, List<ClashWar>> ClashWars { get; set; } = new ConcurrentDictionary<ulong, List<ClashWar>>();
|
||||
public static ConcurrentDictionary<ulong, List<ClashWar>> ClashWars { get; set; }
|
||||
|
||||
private static Timer checkWarTimer { get; }
|
||||
|
||||
private static new readonly Logger _log;
|
||||
|
||||
static ClashOfClans()
|
||||
{
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
var sw = Stopwatch.StartNew();
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
{
|
||||
ClashWars = new ConcurrentDictionary<ulong, List<ClashWar>>(
|
||||
@ -73,7 +67,11 @@ namespace NadekoBot.Modules.ClashOfClans
|
||||
try
|
||||
{
|
||||
SaveWar(war);
|
||||
await war.Channel.SendErrorAsync($"❗🔰**Claim from @{Bases[i].CallUser} for a war against {war.ShortPrint()} has expired.**").ConfigureAwait(false);
|
||||
await war.Channel.SendErrorAsync(GetTextStatic("claim_expired",
|
||||
NadekoBot.Localization.GetCultureInfo(war.Channel.GuildId),
|
||||
typeof(ClashOfClans).Name.ToLowerInvariant(),
|
||||
Format.Bold(Bases[i].CallUser),
|
||||
war.ShortPrint()));
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
@ -82,17 +80,15 @@ namespace NadekoBot.Modules.ClashOfClans
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[RequireUserPermission(GuildPermission.ManageMessages)]
|
||||
public async Task CreateWar(int size, [Remainder] string enemyClan = null)
|
||||
{
|
||||
if (!(Context.User as IGuildUser).GuildPermissions.ManageChannels)
|
||||
return;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(enemyClan))
|
||||
return;
|
||||
|
||||
if (size < 10 || size > 50 || size % 5 != 0)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("🔰 Not a Valid war size").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("invalid_size").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
List<ClashWar> wars;
|
||||
@ -107,7 +103,7 @@ namespace NadekoBot.Modules.ClashOfClans
|
||||
var cw = await CreateWar(enemyClan, size, Context.Guild.Id, Context.Channel.Id);
|
||||
|
||||
wars.Add(cw);
|
||||
await Context.Channel.SendConfirmAsync($"❗🔰**CREATED CLAN WAR AGAINST {cw.ShortPrint()}**").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("war_created", cw.ShortPrint()).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -120,18 +116,18 @@ namespace NadekoBot.Modules.ClashOfClans
|
||||
var warsInfo = GetWarInfo(Context.Guild, num);
|
||||
if (warsInfo == null)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("🔰 **That war does not exist.**").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("war_not_exist").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
var war = warsInfo.Item1[warsInfo.Item2];
|
||||
try
|
||||
{
|
||||
war.Start();
|
||||
await Context.Channel.SendConfirmAsync($"🔰**STARTED WAR AGAINST {war.ShortPrint()}**").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("war_started", war.ShortPrint()).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await Context.Channel.SendErrorAsync($"🔰**WAR AGAINST {war.ShortPrint()} HAS ALREADY STARTED**").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("war_already_started", war.ShortPrint()).ConfigureAwait(false);
|
||||
}
|
||||
SaveWar(war);
|
||||
}
|
||||
@ -149,22 +145,20 @@ namespace NadekoBot.Modules.ClashOfClans
|
||||
ClashWars.TryGetValue(Context.Guild.Id, out wars);
|
||||
if (wars == null || wars.Count == 0)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("🔰 **No active wars.**").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("no_active_wars").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine("🔰 **LIST OF ACTIVE WARS**");
|
||||
sb.AppendLine("**-------------------------**");
|
||||
for (var i = 0; i < wars.Count; i++)
|
||||
{
|
||||
sb.AppendLine($"**#{i + 1}.** `Enemy:` **{wars[i].EnemyClan}**");
|
||||
sb.AppendLine($"\t\t`Size:` **{wars[i].Size} v {wars[i].Size}**");
|
||||
sb.AppendLine($"**#{i + 1}.** `{GetText("enemy")}:` **{wars[i].EnemyClan}**");
|
||||
sb.AppendLine($"\t\t`{GetText("size")}:` **{wars[i].Size} v {wars[i].Size}**");
|
||||
sb.AppendLine("**-------------------------**");
|
||||
}
|
||||
await Context.Channel.SendConfirmAsync(sb.ToString()).ConfigureAwait(false);
|
||||
await Context.Channel.SendConfirmAsync(GetText("list_active_wars"), sb.ToString()).ConfigureAwait(false);
|
||||
return;
|
||||
|
||||
}
|
||||
var num = 0;
|
||||
int.TryParse(number, out num);
|
||||
@ -172,10 +166,11 @@ namespace NadekoBot.Modules.ClashOfClans
|
||||
var warsInfo = GetWarInfo(Context.Guild, num);
|
||||
if (warsInfo == null)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("🔰 **That war does not exist.**").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("war_not_exist").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
await Context.Channel.SendConfirmAsync(warsInfo.Item1[warsInfo.Item2].ToPrettyString()).ConfigureAwait(false);
|
||||
var war = warsInfo.Item1[warsInfo.Item2];
|
||||
await Context.Channel.SendConfirmAsync(war.Localize("info_about_war", $"`{war.EnemyClan}` ({war.Size} v {war.Size})"), war.ToPrettyString()).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -185,7 +180,7 @@ namespace NadekoBot.Modules.ClashOfClans
|
||||
var warsInfo = GetWarInfo(Context.Guild, number);
|
||||
if (warsInfo == null || warsInfo.Item1.Count == 0)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("🔰 **That war does not exist.**").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("war_not_exist").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
var usr =
|
||||
@ -197,11 +192,11 @@ namespace NadekoBot.Modules.ClashOfClans
|
||||
var war = warsInfo.Item1[warsInfo.Item2];
|
||||
war.Call(usr, baseNumber - 1);
|
||||
SaveWar(war);
|
||||
await Context.Channel.SendConfirmAsync($"🔰**{usr}** claimed a base #{baseNumber} for a war against {war.ShortPrint()}").ConfigureAwait(false);
|
||||
await ConfirmLocalized("claimed_base", Format.Bold(usr.ToString()), baseNumber, war.ShortPrint()).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync($"🔰 {ex.Message}").ConfigureAwait(false);
|
||||
await Context.Channel.SendErrorAsync(ex.Message).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -233,15 +228,14 @@ namespace NadekoBot.Modules.ClashOfClans
|
||||
var warsInfo = GetWarInfo(Context.Guild, number);
|
||||
if (warsInfo == null)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("🔰 That war does not exist.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("war_not_exist").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
var war = warsInfo.Item1[warsInfo.Item2];
|
||||
war.End();
|
||||
SaveWar(war);
|
||||
await Context.Channel.SendConfirmAsync($"❗🔰**War against {warsInfo.Item1[warsInfo.Item2].ShortPrint()} ended.**").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("war_ended", warsInfo.Item1[warsInfo.Item2].ShortPrint()).ConfigureAwait(false);
|
||||
|
||||
var size = warsInfo.Item1[warsInfo.Item2].Size;
|
||||
warsInfo.Item1.RemoveAt(warsInfo.Item2);
|
||||
}
|
||||
|
||||
@ -252,7 +246,7 @@ namespace NadekoBot.Modules.ClashOfClans
|
||||
var warsInfo = GetWarInfo(Context.Guild, number);
|
||||
if (warsInfo == null || warsInfo.Item1.Count == 0)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("🔰 **That war does not exist.**").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("war_not_exist").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
var usr =
|
||||
@ -264,11 +258,11 @@ namespace NadekoBot.Modules.ClashOfClans
|
||||
var war = warsInfo.Item1[warsInfo.Item2];
|
||||
var baseNumber = war.Uncall(usr);
|
||||
SaveWar(war);
|
||||
await Context.Channel.SendConfirmAsync($"🔰 @{usr} has **UNCLAIMED** a base #{baseNumber + 1} from a war against {war.ShortPrint()}").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("base_unclaimed", usr, baseNumber + 1, war.ShortPrint()).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync($"🔰 {ex.Message}").ConfigureAwait(false);
|
||||
await Context.Channel.SendErrorAsync(ex.Message).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -277,7 +271,7 @@ namespace NadekoBot.Modules.ClashOfClans
|
||||
var warInfo = GetWarInfo(Context.Guild, number);
|
||||
if (warInfo == null || warInfo.Item1.Count == 0)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("🔰 **That war does not exist.**").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("war_not_exist").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
var war = warInfo.Item1[warInfo.Item2];
|
||||
@ -292,7 +286,7 @@ namespace NadekoBot.Modules.ClashOfClans
|
||||
{
|
||||
war.FinishClaim(baseNumber, stars);
|
||||
}
|
||||
await Context.Channel.SendConfirmAsync($"❗🔰{Context.User.Mention} **DESTROYED** a base #{baseNumber + 1} in a war against {war.ShortPrint()}").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("base_destroyed", baseNumber +1, war.ShortPrint()).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -27,13 +27,13 @@ namespace NadekoBot.Modules.ClashOfClans
|
||||
public static void Call(this ClashWar cw, string u, int baseNumber)
|
||||
{
|
||||
if (baseNumber < 0 || baseNumber >= cw.Bases.Count)
|
||||
throw new ArgumentException("Invalid base number");
|
||||
throw new ArgumentException(cw.Localize("invalid_base_number"));
|
||||
if (cw.Bases[baseNumber].CallUser != null && cw.Bases[baseNumber].Stars == 3)
|
||||
throw new ArgumentException("That base is already destroyed.");
|
||||
throw new ArgumentException(cw.Localize("base_already_claimed"));
|
||||
for (var i = 0; i < cw.Bases.Count; i++)
|
||||
{
|
||||
if (cw.Bases[i]?.BaseDestroyed == false && cw.Bases[i]?.CallUser == u)
|
||||
throw new ArgumentException($"@{u} You already claimed base #{i + 1}. You can't claim a new one.");
|
||||
throw new ArgumentException(cw.Localize("claimed_other", u, i + 1));
|
||||
}
|
||||
|
||||
var cc = cw.Bases[baseNumber];
|
||||
@ -45,7 +45,7 @@ namespace NadekoBot.Modules.ClashOfClans
|
||||
public static void Start(this ClashWar cw)
|
||||
{
|
||||
if (cw.WarState == StateOfWar.Started)
|
||||
throw new InvalidOperationException("War already started");
|
||||
throw new InvalidOperationException("war_already_started");
|
||||
//if (Started)
|
||||
// throw new InvalidOperationException();
|
||||
//Started = true;
|
||||
@ -66,7 +66,7 @@ namespace NadekoBot.Modules.ClashOfClans
|
||||
cw.Bases[i].CallUser = null;
|
||||
return i;
|
||||
}
|
||||
throw new InvalidOperationException("You are not participating in that war.");
|
||||
throw new InvalidOperationException(cw.Localize("not_partic"));
|
||||
}
|
||||
|
||||
public static string ShortPrint(this ClashWar cw) =>
|
||||
@ -75,8 +75,7 @@ namespace NadekoBot.Modules.ClashOfClans
|
||||
public static string ToPrettyString(this ClashWar cw)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
sb.AppendLine($"🔰**WAR AGAINST `{cw.EnemyClan}` ({cw.Size} v {cw.Size}) INFO:**");
|
||||
|
||||
if (cw.WarState == StateOfWar.Created)
|
||||
sb.AppendLine("`not started`");
|
||||
var twoHours = new TimeSpan(2, 0, 0);
|
||||
@ -84,7 +83,7 @@ namespace NadekoBot.Modules.ClashOfClans
|
||||
{
|
||||
if (cw.Bases[i].CallUser == null)
|
||||
{
|
||||
sb.AppendLine($"`{i + 1}.` ❌*unclaimed*");
|
||||
sb.AppendLine($"`{i + 1}.` ❌*{cw.Localize("not_claimed")}*");
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -120,7 +119,7 @@ namespace NadekoBot.Modules.ClashOfClans
|
||||
cw.Bases[i].Stars = stars;
|
||||
return i;
|
||||
}
|
||||
throw new InvalidOperationException($"@{user} You are either not participating in that war, or you already destroyed a base.");
|
||||
throw new InvalidOperationException(cw.Localize("not_partic_or_destroyed", user));
|
||||
}
|
||||
|
||||
public static void FinishClaim(this ClashWar cw, int index, int stars = 3)
|
||||
@ -128,10 +127,22 @@ namespace NadekoBot.Modules.ClashOfClans
|
||||
if (index < 0 || index > cw.Bases.Count)
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
var toFinish = cw.Bases[index];
|
||||
if (toFinish.BaseDestroyed != false) throw new InvalidOperationException("That base is already destroyed.");
|
||||
if (toFinish.CallUser == null) throw new InvalidOperationException("That base is unclaimed.");
|
||||
if (toFinish.BaseDestroyed != false) throw new InvalidOperationException(cw.Localize("base_already_destroyed"));
|
||||
if (toFinish.CallUser == null) throw new InvalidOperationException(cw.Localize("base_already_unclaimed"));
|
||||
toFinish.BaseDestroyed = true;
|
||||
toFinish.Stars = stars;
|
||||
}
|
||||
|
||||
public static string Localize(this ClashWar cw, string key)
|
||||
{
|
||||
return NadekoTopLevelModule.GetTextStatic(key,
|
||||
NadekoBot.Localization.GetCultureInfo(cw.Channel?.GuildId),
|
||||
typeof(ClashOfClans).Name.ToLowerInvariant());
|
||||
}
|
||||
|
||||
public static string Localize(this ClashWar cw, string key, params object[] replacements)
|
||||
{
|
||||
return string.Format(cw.Localize(key), replacements);
|
||||
}
|
||||
}
|
||||
}
|
@ -11,11 +11,13 @@ using NLog;
|
||||
using System.Diagnostics;
|
||||
using Discord.WebSocket;
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using NadekoBot.DataStructures;
|
||||
|
||||
namespace NadekoBot.Modules.CustomReactions
|
||||
{
|
||||
[NadekoModule("CustomReactions", ".")]
|
||||
public class CustomReactions : DiscordModule
|
||||
public class CustomReactions : NadekoTopLevelModule
|
||||
{
|
||||
private static CustomReaction[] _globalReactions = new CustomReaction[] { };
|
||||
public static CustomReaction[] GlobalReactions => _globalReactions;
|
||||
@ -69,7 +71,22 @@ namespace NadekoBot.Modules.CustomReactions
|
||||
if (reaction != null)
|
||||
{
|
||||
if (reaction.Response != "-")
|
||||
try { await channel.SendMessageAsync(reaction.ResponseWithContext(umsg)).ConfigureAwait(false); } catch { }
|
||||
{
|
||||
CREmbed crembed;
|
||||
if (CREmbed.TryParse(reaction.Response, out crembed))
|
||||
{
|
||||
try { await channel.EmbedAsync(crembed.ToEmbed(), crembed.PlainText ?? "").ConfigureAwait(false); }
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.Warn("Sending CREmbed failed");
|
||||
_log.Warn(ex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
try { await channel.SendMessageAsync(reaction.ResponseWithContext(umsg)).ConfigureAwait(false); } catch { }
|
||||
}
|
||||
}
|
||||
|
||||
ReactionStats.AddOrUpdate(reaction.Trigger, 1, (k, old) => ++old);
|
||||
return true;
|
||||
@ -91,7 +108,20 @@ namespace NadekoBot.Modules.CustomReactions
|
||||
|
||||
if (greaction != null)
|
||||
{
|
||||
try { await channel.SendMessageAsync(greaction.ResponseWithContext(umsg)).ConfigureAwait(false); } catch { }
|
||||
CREmbed crembed;
|
||||
if (CREmbed.TryParse(greaction.Response, out crembed))
|
||||
{
|
||||
try { await channel.EmbedAsync(crembed.ToEmbed(), crembed.PlainText ?? "").ConfigureAwait(false); }
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.Warn("Sending CREmbed failed");
|
||||
_log.Warn(ex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
try { await channel.SendMessageAsync(greaction.ResponseWithContext(umsg)).ConfigureAwait(false); } catch { }
|
||||
}
|
||||
ReactionStats.AddOrUpdate(greaction.Trigger, 1, (k, old) => ++old);
|
||||
return true;
|
||||
}
|
||||
@ -109,7 +139,7 @@ namespace NadekoBot.Modules.CustomReactions
|
||||
|
||||
if ((channel == null && !NadekoBot.Credentials.IsOwner(Context.User)) || (channel != null && !((IGuildUser)Context.User).GuildPermissions.Administrator))
|
||||
{
|
||||
try { await Context.Channel.SendErrorAsync("Insufficient permissions. Requires Bot ownership for global custom reactions, and Administrator for guild custom reactions."); } catch { }
|
||||
await ReplyErrorLocalized("insuff_perms").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -135,8 +165,8 @@ namespace NadekoBot.Modules.CustomReactions
|
||||
}
|
||||
else
|
||||
{
|
||||
var reactions = GuildReactions.AddOrUpdate(Context.Guild.Id,
|
||||
Array.Empty<CustomReaction>(),
|
||||
GuildReactions.AddOrUpdate(Context.Guild.Id,
|
||||
new CustomReaction[] { cr },
|
||||
(k, old) =>
|
||||
{
|
||||
Array.Resize(ref old, old.Length + 1);
|
||||
@ -146,10 +176,10 @@ namespace NadekoBot.Modules.CustomReactions
|
||||
}
|
||||
|
||||
await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||
.WithTitle("New Custom Reaction")
|
||||
.WithTitle(GetText("new_cust_react"))
|
||||
.WithDescription($"#{cr.Id}")
|
||||
.AddField(efb => efb.WithName("Trigger").WithValue(key))
|
||||
.AddField(efb => efb.WithName("Response").WithValue(message))
|
||||
.AddField(efb => efb.WithName(GetText("trigger")).WithValue(key))
|
||||
.AddField(efb => efb.WithName(GetText("response")).WithValue(message))
|
||||
).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@ -166,19 +196,20 @@ namespace NadekoBot.Modules.CustomReactions
|
||||
customReactions = GuildReactions.GetOrAdd(Context.Guild.Id, Array.Empty<CustomReaction>()).Where(cr => cr != null).ToArray();
|
||||
|
||||
if (customReactions == null || !customReactions.Any())
|
||||
await Context.Channel.SendErrorAsync("No custom reactions found").ConfigureAwait(false);
|
||||
else
|
||||
{
|
||||
var lastPage = customReactions.Length / 20;
|
||||
await Context.Channel.SendPaginatedConfirmAsync(page, curPage =>
|
||||
new EmbedBuilder().WithOkColor()
|
||||
.WithTitle("Custom reactions")
|
||||
.WithDescription(string.Join("\n", customReactions.OrderBy(cr => cr.Trigger)
|
||||
.Skip((curPage - 1) * 20)
|
||||
.Take(20)
|
||||
.Select(cr => $"`#{cr.Id}` `Trigger:` {cr.Trigger}"))), lastPage)
|
||||
.ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("no_found").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
var lastPage = customReactions.Length / 20;
|
||||
await Context.Channel.SendPaginatedConfirmAsync(page, curPage =>
|
||||
new EmbedBuilder().WithOkColor()
|
||||
.WithTitle(GetText("name"))
|
||||
.WithDescription(string.Join("\n", customReactions.OrderBy(cr => cr.Trigger)
|
||||
.Skip((curPage - 1) * 20)
|
||||
.Take(20)
|
||||
.Select(cr => $"`#{cr.Id}` `{GetText("trigger")}:` {cr.Trigger}"))), lastPage)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public enum All
|
||||
@ -197,20 +228,22 @@ namespace NadekoBot.Modules.CustomReactions
|
||||
customReactions = GuildReactions.GetOrAdd(Context.Guild.Id, new CustomReaction[]{ }).Where(cr => cr != null).ToArray();
|
||||
|
||||
if (customReactions == null || !customReactions.Any())
|
||||
await Context.Channel.SendErrorAsync("No custom reactions found").ConfigureAwait(false);
|
||||
else
|
||||
{
|
||||
var txtStream = await customReactions.GroupBy(cr => cr.Trigger)
|
||||
.OrderBy(cr => cr.Key)
|
||||
.Select(cr => new { Trigger = cr.Key, Responses = cr.Select(y => new { id = y.Id, text = y.Response }).ToList() })
|
||||
.ToJson()
|
||||
.ToStream()
|
||||
.ConfigureAwait(false);
|
||||
if (Context.Guild == null) // its a private one, just send back
|
||||
await Context.Channel.SendFileAsync(txtStream, "customreactions.txt", "List of all custom reactions").ConfigureAwait(false);
|
||||
else
|
||||
await ((IGuildUser)Context.User).SendFileAsync(txtStream, "customreactions.txt", "List of all custom reactions").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("no_found").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
var txtStream = await customReactions.GroupBy(cr => cr.Trigger)
|
||||
.OrderBy(cr => cr.Key)
|
||||
.Select(cr => new { Trigger = cr.Key, Responses = cr.Select(y => new { id = y.Id, text = y.Response }).ToList() })
|
||||
.ToJson()
|
||||
.ToStream()
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (Context.Guild == null) // its a private one, just send back
|
||||
await Context.Channel.SendFileAsync(txtStream, "customreactions.txt", GetText("list_all")).ConfigureAwait(false);
|
||||
else
|
||||
await ((IGuildUser)Context.User).SendFileAsync(txtStream, "customreactions.txt", GetText("list_all")).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -225,7 +258,9 @@ namespace NadekoBot.Modules.CustomReactions
|
||||
customReactions = GuildReactions.GetOrAdd(Context.Guild.Id, new CustomReaction[]{ }).Where(cr => cr != null).ToArray();
|
||||
|
||||
if (customReactions == null || !customReactions.Any())
|
||||
await Context.Channel.SendErrorAsync("No custom reactions found").ConfigureAwait(false);
|
||||
{
|
||||
await ReplyErrorLocalized("no_found").ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
var ordered = customReactions
|
||||
@ -236,7 +271,7 @@ namespace NadekoBot.Modules.CustomReactions
|
||||
var lastPage = ordered.Count / 20;
|
||||
await Context.Channel.SendPaginatedConfirmAsync(page, (curPage) =>
|
||||
new EmbedBuilder().WithOkColor()
|
||||
.WithTitle($"Custom Reactions (grouped)")
|
||||
.WithTitle(GetText("name"))
|
||||
.WithDescription(string.Join("\r\n", ordered
|
||||
.Skip((curPage - 1) * 20)
|
||||
.Take(20)
|
||||
@ -257,13 +292,16 @@ namespace NadekoBot.Modules.CustomReactions
|
||||
var found = customReactions.FirstOrDefault(cr => cr?.Id == id);
|
||||
|
||||
if (found == null)
|
||||
await Context.Channel.SendErrorAsync("No custom reaction found with that id.").ConfigureAwait(false);
|
||||
{
|
||||
await ReplyErrorLocalized("no_found_id").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||
.WithDescription($"#{id}")
|
||||
.AddField(efb => efb.WithName("Trigger").WithValue(found.Trigger))
|
||||
.AddField(efb => efb.WithName("Response").WithValue(found.Response + "\n```css\n" + found.Response + "```"))
|
||||
.AddField(efb => efb.WithName(GetText("trigger")).WithValue(found.Trigger))
|
||||
.AddField(efb => efb.WithName(GetText("response")).WithValue(found.Response + "\n```css\n" + found.Response + "```"))
|
||||
).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@ -273,7 +311,7 @@ namespace NadekoBot.Modules.CustomReactions
|
||||
{
|
||||
if ((Context.Guild == null && !NadekoBot.Credentials.IsOwner(Context.User)) || (Context.Guild != null && !((IGuildUser)Context.User).GuildPermissions.Administrator))
|
||||
{
|
||||
try { await Context.Channel.SendErrorAsync("Insufficient permissions. Requires Bot ownership for global custom reactions, and Administrator for guild custom reactions."); } catch { }
|
||||
await ReplyErrorLocalized("insuff_perms").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -283,32 +321,42 @@ namespace NadekoBot.Modules.CustomReactions
|
||||
{
|
||||
toDelete = uow.CustomReactions.Get(id);
|
||||
if (toDelete == null) //not found
|
||||
return;
|
||||
|
||||
if ((toDelete.GuildId == null || toDelete.GuildId == 0) && Context.Guild == null)
|
||||
success = false;
|
||||
else
|
||||
{
|
||||
uow.CustomReactions.Remove(toDelete);
|
||||
//todo i can dramatically improve performance of this, if Ids are ordered.
|
||||
_globalReactions = GlobalReactions.Where(cr => cr?.Id != toDelete.Id).ToArray();
|
||||
success = true;
|
||||
}
|
||||
else if ((toDelete.GuildId != null && toDelete.GuildId != 0) && Context.Guild.Id == toDelete.GuildId)
|
||||
{
|
||||
uow.CustomReactions.Remove(toDelete);
|
||||
GuildReactions.AddOrUpdate(Context.Guild.Id, new CustomReaction[] { }, (key, old) =>
|
||||
if ((toDelete.GuildId == null || toDelete.GuildId == 0) && Context.Guild == null)
|
||||
{
|
||||
return old.Where(cr => cr?.Id != toDelete.Id).ToArray();
|
||||
});
|
||||
success = true;
|
||||
uow.CustomReactions.Remove(toDelete);
|
||||
//todo i can dramatically improve performance of this, if Ids are ordered.
|
||||
_globalReactions = GlobalReactions.Where(cr => cr?.Id != toDelete.Id).ToArray();
|
||||
success = true;
|
||||
}
|
||||
else if ((toDelete.GuildId != null && toDelete.GuildId != 0) && Context.Guild.Id == toDelete.GuildId)
|
||||
{
|
||||
uow.CustomReactions.Remove(toDelete);
|
||||
GuildReactions.AddOrUpdate(Context.Guild.Id, new CustomReaction[] { }, (key, old) =>
|
||||
{
|
||||
return old.Where(cr => cr?.Id != toDelete.Id).ToArray();
|
||||
});
|
||||
success = true;
|
||||
}
|
||||
if (success)
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
if (success)
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (success)
|
||||
await Context.Channel.SendConfirmAsync("Deleted custom reaction", toDelete.ToString()).ConfigureAwait(false);
|
||||
{
|
||||
await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||
.WithTitle(GetText("deleted"))
|
||||
.WithDescription("#" + toDelete.Id)
|
||||
.AddField(efb => efb.WithName(GetText("trigger")).WithValue(toDelete.Trigger))
|
||||
.AddField(efb => efb.WithName(GetText("response")).WithValue(toDelete.Response)));
|
||||
}
|
||||
else
|
||||
await Context.Channel.SendErrorAsync("Failed to find that custom reaction.").ConfigureAwait(false);
|
||||
{
|
||||
await ReplyErrorLocalized("no_found_id").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -318,18 +366,18 @@ namespace NadekoBot.Modules.CustomReactions
|
||||
if (string.IsNullOrWhiteSpace(trigger))
|
||||
{
|
||||
ClearStats();
|
||||
await Context.Channel.SendConfirmAsync($"Custom reaction stats cleared.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("all_stats_cleared").ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
uint throwaway;
|
||||
if (ReactionStats.TryRemove(trigger, out throwaway))
|
||||
{
|
||||
await Context.Channel.SendConfirmAsync($"Stats cleared for `{trigger}` custom reaction.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("stats_cleared", Format.Bold(trigger)).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("No stats for that trigger found, no action taken.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("stats_not_found").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -346,7 +394,7 @@ namespace NadekoBot.Modules.CustomReactions
|
||||
await Context.Channel.SendPaginatedConfirmAsync(page,
|
||||
(curPage) => ordered.Skip((curPage - 1) * 9)
|
||||
.Take(9)
|
||||
.Aggregate(new EmbedBuilder().WithOkColor().WithTitle($"Custom Reaction Stats"),
|
||||
.Aggregate(new EmbedBuilder().WithOkColor().WithTitle(GetText("stats")),
|
||||
(agg, cur) => agg.AddField(efb => efb.WithName(cur.Key).WithValue(cur.Value.ToString()).WithIsInline(true))), lastPage)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
@ -23,17 +23,24 @@ namespace NadekoBot.Modules.CustomReactions
|
||||
{"%mention%", (ctx) => { return $"<@{NadekoBot.Client.CurrentUser.Id}>"; } },
|
||||
{"%user%", (ctx) => { return ctx.Author.Mention; } },
|
||||
{"%rnduser%", (ctx) => {
|
||||
var ch = ctx.Channel as ITextChannel;
|
||||
if(ch == null)
|
||||
return "";
|
||||
//var ch = ctx.Channel as ITextChannel;
|
||||
//if(ch == null)
|
||||
// return "";
|
||||
|
||||
var g = ch.Guild as SocketGuild;
|
||||
if(g == null)
|
||||
return "";
|
||||
//var g = ch.Guild as SocketGuild;
|
||||
//if(g == null)
|
||||
// return "";
|
||||
//try {
|
||||
// var usr = g.Users.Skip(new NadekoRandom().Next(0, g.Users.Count)).FirstOrDefault();
|
||||
// return usr.Mention;
|
||||
//}
|
||||
//catch {
|
||||
return "[%rnduser% is temp. disabled]";
|
||||
//}
|
||||
|
||||
var users = g.Users.ToArray();
|
||||
//var users = g.Users.ToArray();
|
||||
|
||||
return users[new NadekoRandom().Next(0, users.Length-1)].Mention;
|
||||
//return users[new NadekoRandom().Next(0, users.Length-1)].Mention;
|
||||
} }
|
||||
//{"%rng%", (ctx) => { return new NadekoRandom().Next(0,10).ToString(); } }
|
||||
};
|
||||
|
@ -1,22 +0,0 @@
|
||||
using Discord.Commands;
|
||||
using NLog;
|
||||
|
||||
namespace NadekoBot.Modules
|
||||
{
|
||||
public abstract class DiscordModule : ModuleBase
|
||||
{
|
||||
protected Logger _log { get; }
|
||||
protected string _prefix { get; }
|
||||
|
||||
public DiscordModule()
|
||||
{
|
||||
string prefix;
|
||||
if (NadekoBot.ModulePrefixes.TryGetValue(this.GetType().Name, out prefix))
|
||||
_prefix = prefix;
|
||||
else
|
||||
_prefix = "?missing_prefix?";
|
||||
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
}
|
||||
}
|
||||
}
|
@ -17,7 +17,7 @@ namespace NadekoBot.Modules.Gambling
|
||||
public partial class Gambling
|
||||
{
|
||||
[Group]
|
||||
public class AnimalRacing : ModuleBase
|
||||
public class AnimalRacing : NadekoSubmodule
|
||||
{
|
||||
public static ConcurrentDictionary<ulong, AnimalRace> AnimalRaces { get; } = new ConcurrentDictionary<ulong, AnimalRace>();
|
||||
|
||||
@ -25,10 +25,10 @@ namespace NadekoBot.Modules.Gambling
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task Race()
|
||||
{
|
||||
var ar = new AnimalRace(Context.Guild.Id, (ITextChannel)Context.Channel);
|
||||
var ar = new AnimalRace(Context.Guild.Id, (ITextChannel)Context.Channel, Prefix);
|
||||
|
||||
if (ar.Fail)
|
||||
await Context.Channel.SendErrorAsync("🏁 `Failed starting a race. Another race is probably running.`").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("race_failed_starting").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -43,7 +43,7 @@ namespace NadekoBot.Modules.Gambling
|
||||
AnimalRace ar;
|
||||
if (!AnimalRaces.TryGetValue(Context.Guild.Id, out ar))
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("No race exists on this server").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("race_not_exist").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
await ar.JoinRace(Context.User as IGuildUser, amount);
|
||||
@ -56,29 +56,29 @@ namespace NadekoBot.Modules.Gambling
|
||||
|
||||
public bool Fail { get; set; }
|
||||
|
||||
public List<Participant> participants = new List<Participant>();
|
||||
private ulong serverId;
|
||||
private int messagesSinceGameStarted = 0;
|
||||
private Logger _log { get; }
|
||||
private readonly List<Participant> _participants = new List<Participant>();
|
||||
private readonly ulong _serverId;
|
||||
private int _messagesSinceGameStarted;
|
||||
private readonly string _prefix;
|
||||
|
||||
public ITextChannel raceChannel { get; set; }
|
||||
public bool Started { get; private set; } = false;
|
||||
private readonly Logger _log;
|
||||
|
||||
public AnimalRace(ulong serverId, ITextChannel ch)
|
||||
private readonly ITextChannel _raceChannel;
|
||||
public bool Started { get; private set; }
|
||||
|
||||
public AnimalRace(ulong serverId, ITextChannel ch, string prefix)
|
||||
{
|
||||
this._log = LogManager.GetCurrentClassLogger();
|
||||
this.serverId = serverId;
|
||||
this.raceChannel = ch;
|
||||
_prefix = prefix;
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
_serverId = serverId;
|
||||
_raceChannel = ch;
|
||||
if (!AnimalRaces.TryAdd(serverId, this))
|
||||
{
|
||||
Fail = true;
|
||||
return;
|
||||
}
|
||||
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
{
|
||||
animals = new ConcurrentQueue<string>(NadekoBot.BotConfig.RaceAnimals.Select(ra => ra.Icon).Shuffle());
|
||||
}
|
||||
|
||||
animals = new ConcurrentQueue<string>(NadekoBot.BotConfig.RaceAnimals.Select(ra => ra.Icon).Shuffle());
|
||||
|
||||
|
||||
var cancelSource = new CancellationTokenSource();
|
||||
@ -90,8 +90,8 @@ namespace NadekoBot.Modules.Gambling
|
||||
{
|
||||
try
|
||||
{
|
||||
await raceChannel.SendConfirmAsync("Animal Race", $"Starting in 20 seconds or when the room is full.",
|
||||
footer: $"Type {NadekoBot.ModulePrefixes[typeof(Gambling).Name]}jr to join the race.");
|
||||
await _raceChannel.SendConfirmAsync(GetText("animal_race"), GetText("animal_race_starting"),
|
||||
footer: GetText("animal_race_join_instr", _prefix));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -102,16 +102,16 @@ namespace NadekoBot.Modules.Gambling
|
||||
cancelSource.Cancel();
|
||||
if (t == fullgame)
|
||||
{
|
||||
try { await raceChannel.SendConfirmAsync("Animal Race", "Full! Starting immediately."); } catch (Exception ex) { _log.Warn(ex); }
|
||||
try { await _raceChannel.SendConfirmAsync(GetText("animal_race"), GetText("animal_race_full") ); } catch (Exception ex) { _log.Warn(ex); }
|
||||
}
|
||||
else if (participants.Count > 1)
|
||||
else if (_participants.Count > 1)
|
||||
{
|
||||
try { await raceChannel.SendConfirmAsync("Animal Race", "Starting with " + participants.Count + " participants."); } catch (Exception ex) { _log.Warn(ex); }
|
||||
try { await _raceChannel.SendConfirmAsync(GetText("animal_race"), GetText("animal_race_starting_with_x", _participants.Count)); } catch (Exception ex) { _log.Warn(ex); }
|
||||
}
|
||||
else
|
||||
{
|
||||
try { await raceChannel.SendErrorAsync("Animal Race", "Failed to start since there was not enough participants."); } catch (Exception ex) { _log.Warn(ex); }
|
||||
var p = participants.FirstOrDefault();
|
||||
try { await _raceChannel.SendErrorAsync(GetText("animal_race"), GetText("animal_race_failed")); } catch (Exception ex) { _log.Warn(ex); }
|
||||
var p = _participants.FirstOrDefault();
|
||||
|
||||
if (p != null && p.AmountBet > 0)
|
||||
await CurrencyHandler.AddCurrencyAsync(p.User, "BetRace", p.AmountBet, false).ConfigureAwait(false);
|
||||
@ -128,7 +128,7 @@ namespace NadekoBot.Modules.Gambling
|
||||
private void End()
|
||||
{
|
||||
AnimalRace throwaway;
|
||||
AnimalRaces.TryRemove(serverId, out throwaway);
|
||||
AnimalRaces.TryRemove(_serverId, out throwaway);
|
||||
}
|
||||
|
||||
private async Task StartRace()
|
||||
@ -136,21 +136,21 @@ namespace NadekoBot.Modules.Gambling
|
||||
var rng = new NadekoRandom();
|
||||
Participant winner = null;
|
||||
IUserMessage msg = null;
|
||||
int place = 1;
|
||||
var place = 1;
|
||||
try
|
||||
{
|
||||
NadekoBot.Client.MessageReceived += Client_MessageReceived;
|
||||
|
||||
while (!participants.All(p => p.Total >= 60))
|
||||
while (!_participants.All(p => p.Total >= 60))
|
||||
{
|
||||
//update the state
|
||||
participants.ForEach(p =>
|
||||
_participants.ForEach(p =>
|
||||
{
|
||||
p.Total += 1 + rng.Next(0, 10);
|
||||
});
|
||||
|
||||
|
||||
participants
|
||||
_participants
|
||||
.OrderByDescending(p => p.Total)
|
||||
.ForEach(p =>
|
||||
{
|
||||
@ -170,14 +170,14 @@ namespace NadekoBot.Modules.Gambling
|
||||
//draw the state
|
||||
|
||||
var text = $@"|🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🔚|
|
||||
{String.Join("\n", participants.Select(p => $"{(int)(p.Total / 60f * 100),-2}%|{p.ToString()}"))}
|
||||
{String.Join("\n", _participants.Select(p => $"{(int)(p.Total / 60f * 100),-2}%|{p.ToString()}"))}
|
||||
|🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🔚|";
|
||||
if (msg == null || messagesSinceGameStarted >= 10) // also resend the message if channel was spammed
|
||||
if (msg == null || _messagesSinceGameStarted >= 10) // also resend the message if channel was spammed
|
||||
{
|
||||
if (msg != null)
|
||||
try { await msg.DeleteAsync(); } catch { }
|
||||
messagesSinceGameStarted = 0;
|
||||
try { msg = await raceChannel.SendMessageAsync(text).ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
|
||||
_messagesSinceGameStarted = 0;
|
||||
try { msg = await _raceChannel.SendMessageAsync(text).ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -187,22 +187,33 @@ namespace NadekoBot.Modules.Gambling
|
||||
await Task.Delay(2500);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
finally
|
||||
{
|
||||
NadekoBot.Client.MessageReceived -= Client_MessageReceived;
|
||||
}
|
||||
|
||||
if (winner.AmountBet > 0)
|
||||
if (winner != null)
|
||||
{
|
||||
var wonAmount = winner.AmountBet * (participants.Count - 1);
|
||||
if (winner.AmountBet > 0)
|
||||
{
|
||||
var wonAmount = winner.AmountBet * (_participants.Count - 1);
|
||||
|
||||
await CurrencyHandler.AddCurrencyAsync(winner.User, "Won a Race", wonAmount, true).ConfigureAwait(false);
|
||||
await raceChannel.SendConfirmAsync("Animal Race", $"{winner.User.Mention} as {winner.Animal} **Won the race and {wonAmount}{CurrencySign}!**").ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await raceChannel.SendConfirmAsync("Animal Race", $"{winner.User.Mention} as {winner.Animal} **Won the race!**").ConfigureAwait(false);
|
||||
await CurrencyHandler.AddCurrencyAsync(winner.User, "Won a Race", wonAmount, true)
|
||||
.ConfigureAwait(false);
|
||||
await _raceChannel.SendConfirmAsync(GetText("animal_race"),
|
||||
Format.Bold(GetText("animal_race_won_money", winner.User.Mention,
|
||||
winner.Animal, wonAmount + CurrencySign)))
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await _raceChannel.SendConfirmAsync(GetText("animal_race"),
|
||||
Format.Bold(GetText("animal_race_won", winner.User.Mention, winner.Animal))).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -212,9 +223,9 @@ namespace NadekoBot.Modules.Gambling
|
||||
var msg = imsg as SocketUserMessage;
|
||||
if (msg == null)
|
||||
return Task.CompletedTask;
|
||||
if (msg.IsAuthor() || !(imsg.Channel is ITextChannel) || imsg.Channel != raceChannel)
|
||||
if (msg.IsAuthor() || !(imsg.Channel is ITextChannel) || imsg.Channel != _raceChannel)
|
||||
return Task.CompletedTask;
|
||||
messagesSinceGameStarted++;
|
||||
_messagesSinceGameStarted++;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
@ -228,51 +239,66 @@ namespace NadekoBot.Modules.Gambling
|
||||
|
||||
public async Task JoinRace(IGuildUser u, int amount = 0)
|
||||
{
|
||||
var animal = "";
|
||||
string animal;
|
||||
if (!animals.TryDequeue(out animal))
|
||||
{
|
||||
await raceChannel.SendErrorAsync($"{u.Mention} `There is no running race on this server.`").ConfigureAwait(false);
|
||||
await _raceChannel.SendErrorAsync(GetText("animal_race_no_race")).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
var p = new Participant(u, animal, amount);
|
||||
if (participants.Contains(p))
|
||||
if (_participants.Contains(p))
|
||||
{
|
||||
await raceChannel.SendErrorAsync($"{u.Mention} `You already joined this race.`").ConfigureAwait(false);
|
||||
await _raceChannel.SendErrorAsync(GetText("animal_race_already_in")).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
if (Started)
|
||||
{
|
||||
await raceChannel.SendErrorAsync($"{u.Mention} `Race is already started`").ConfigureAwait(false);
|
||||
await _raceChannel.SendErrorAsync(GetText("animal_race_already_started")).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
if (amount > 0)
|
||||
if (!await CurrencyHandler.RemoveCurrencyAsync((IGuildUser)u, "BetRace", amount, false).ConfigureAwait(false))
|
||||
if (!await CurrencyHandler.RemoveCurrencyAsync(u, "BetRace", amount, false).ConfigureAwait(false))
|
||||
{
|
||||
try { await raceChannel.SendErrorAsync($"{u.Mention} You don't have enough {NadekoBot.BotConfig.CurrencyPluralName}.").ConfigureAwait(false); } catch { }
|
||||
await _raceChannel.SendErrorAsync(GetText("not_enough", CurrencySign)).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
participants.Add(p);
|
||||
await raceChannel.SendConfirmAsync("Animal Race", $"{u.Mention} **joined as a {p.Animal}" + (amount > 0 ? $" and bet {amount} {CurrencySign}!**" : "**"))
|
||||
.ConfigureAwait(false);
|
||||
_participants.Add(p);
|
||||
string confStr;
|
||||
if (amount > 0)
|
||||
confStr = GetText("animal_race_join_bet", u.Mention, p.Animal, amount + CurrencySign);
|
||||
else
|
||||
confStr = GetText("animal_race_join", u.Mention, p.Animal);
|
||||
await _raceChannel.SendConfirmAsync(GetText("animal_race"), Format.Bold(confStr)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private string GetText(string text)
|
||||
=> NadekoTopLevelModule.GetTextStatic(text,
|
||||
NadekoBot.Localization.GetCultureInfo(_raceChannel.Guild),
|
||||
typeof(Gambling).Name.ToLowerInvariant());
|
||||
|
||||
private string GetText(string text, params object[] replacements)
|
||||
=> NadekoTopLevelModule.GetTextStatic(text,
|
||||
NadekoBot.Localization.GetCultureInfo(_raceChannel.Guild),
|
||||
typeof(Gambling).Name.ToLowerInvariant(),
|
||||
replacements);
|
||||
}
|
||||
|
||||
public class Participant
|
||||
{
|
||||
public IGuildUser User { get; set; }
|
||||
public string Animal { get; set; }
|
||||
public int AmountBet { get; set; }
|
||||
public IGuildUser User { get; }
|
||||
public string Animal { get; }
|
||||
public int AmountBet { get; }
|
||||
|
||||
public float Coeff { get; set; }
|
||||
public int Total { get; set; }
|
||||
|
||||
public int Place { get; set; } = 0;
|
||||
public int Place { get; set; }
|
||||
|
||||
public Participant(IGuildUser u, string a, int amount)
|
||||
{
|
||||
this.User = u;
|
||||
this.Animal = a;
|
||||
this.AmountBet = amount;
|
||||
User = u;
|
||||
Animal = a;
|
||||
AmountBet = amount;
|
||||
}
|
||||
|
||||
public override int GetHashCode() => User.GetHashCode();
|
||||
@ -280,9 +306,7 @@ namespace NadekoBot.Modules.Gambling
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
var p = obj as Participant;
|
||||
return p == null ?
|
||||
false :
|
||||
p.User == User;
|
||||
return p != null && p.User == User;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
@ -290,23 +314,13 @@ namespace NadekoBot.Modules.Gambling
|
||||
var str = new string('‣', Total) + Animal;
|
||||
if (Place == 0)
|
||||
return str;
|
||||
if (Place == 1)
|
||||
{
|
||||
return str + "🏆";
|
||||
}
|
||||
else if (Place == 2)
|
||||
{
|
||||
return str + "`2nd`";
|
||||
}
|
||||
else if (Place == 3)
|
||||
{
|
||||
return str + "`3rd`";
|
||||
}
|
||||
else
|
||||
{
|
||||
return str + $"`{Place}th`";
|
||||
}
|
||||
|
||||
str += $"`#{Place}`";
|
||||
|
||||
if (Place == 1)
|
||||
str += "🏆";
|
||||
|
||||
return str;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,19 +5,18 @@ using NadekoBot.Extensions;
|
||||
using NadekoBot.Services;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Discord.WebSocket;
|
||||
using NadekoBot.Services.Database;
|
||||
using System.Threading;
|
||||
using NLog;
|
||||
|
||||
namespace NadekoBot.Modules.Gambling
|
||||
{
|
||||
public partial class Gambling
|
||||
{
|
||||
[Group]
|
||||
public class CurrencyEvents : ModuleBase
|
||||
public class CurrencyEvents : NadekoSubmodule
|
||||
{
|
||||
public enum CurrencyEvent
|
||||
{
|
||||
@ -25,8 +24,7 @@ namespace NadekoBot.Modules.Gambling
|
||||
SneakyGameStatus
|
||||
}
|
||||
//flower reaction event
|
||||
public static readonly ConcurrentHashSet<ulong> _flowerReactionAwardedUsers = new ConcurrentHashSet<ulong>();
|
||||
public static readonly ConcurrentHashSet<ulong> _sneakyGameAwardedUsers = new ConcurrentHashSet<ulong>();
|
||||
private static readonly ConcurrentHashSet<ulong> _sneakyGameAwardedUsers = new ConcurrentHashSet<ulong>();
|
||||
|
||||
|
||||
private static readonly char[] _sneakyGameStatusChars = Enumerable.Range(48, 10)
|
||||
@ -35,33 +33,25 @@ namespace NadekoBot.Modules.Gambling
|
||||
.Select(x => (char)x)
|
||||
.ToArray();
|
||||
|
||||
private static string _secretCode = String.Empty;
|
||||
private static string _secretCode = string.Empty;
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[OwnerOnly]
|
||||
public async Task StartEvent(CurrencyEvent e, int arg = -1)
|
||||
{
|
||||
var channel = (ITextChannel)Context.Channel;
|
||||
try
|
||||
switch (e)
|
||||
{
|
||||
|
||||
switch (e)
|
||||
{
|
||||
case CurrencyEvent.FlowerReaction:
|
||||
await FlowerReactionEvent(Context).ConfigureAwait(false);
|
||||
break;
|
||||
case CurrencyEvent.SneakyGameStatus:
|
||||
await SneakyGameStatusEvent(Context, arg).ConfigureAwait(false);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
case CurrencyEvent.FlowerReaction:
|
||||
await FlowerReactionEvent(Context, arg).ConfigureAwait(false);
|
||||
break;
|
||||
case CurrencyEvent.SneakyGameStatus:
|
||||
await SneakyGameStatusEvent(Context, arg).ConfigureAwait(false);
|
||||
break;
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
public static async Task SneakyGameStatusEvent(CommandContext Context, int? arg)
|
||||
public async Task SneakyGameStatusEvent(CommandContext context, int? arg)
|
||||
{
|
||||
int num;
|
||||
if (arg == null || arg < 5)
|
||||
@ -69,35 +59,38 @@ namespace NadekoBot.Modules.Gambling
|
||||
else
|
||||
num = arg.Value;
|
||||
|
||||
if (_secretCode != String.Empty)
|
||||
if (_secretCode != string.Empty)
|
||||
return;
|
||||
var rng = new NadekoRandom();
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
for (var i = 0; i < 5; i++)
|
||||
{
|
||||
_secretCode += _sneakyGameStatusChars[rng.Next(0, _sneakyGameStatusChars.Length)];
|
||||
}
|
||||
|
||||
|
||||
await NadekoBot.Client.SetGameAsync($"type {_secretCode} for " + NadekoBot.BotConfig.CurrencyPluralName)
|
||||
.ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
await Context.Channel.SendConfirmAsync($"SneakyGameStatus event started",
|
||||
$"Users must type a secret code to get 100 currency.\n" +
|
||||
$"Lasts {num} seconds. Don't tell anyone. Shhh.")
|
||||
.ConfigureAwait(false);
|
||||
var title = GetText("sneakygamestatus_title");
|
||||
var desc = GetText("sneakygamestatus_desc", Format.Bold(100.ToString()) + CurrencySign, Format.Bold(num.ToString()));
|
||||
await context.Channel.SendConfirmAsync(title, desc).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
catch { }
|
||||
|
||||
|
||||
NadekoBot.Client.MessageReceived += SneakyGameMessageReceivedEventHandler;
|
||||
await Task.Delay(num * 1000);
|
||||
NadekoBot.Client.MessageReceived -= SneakyGameMessageReceivedEventHandler;
|
||||
|
||||
var cnt = _sneakyGameAwardedUsers.Count;
|
||||
_sneakyGameAwardedUsers.Clear();
|
||||
_secretCode = String.Empty;
|
||||
_secretCode = string.Empty;
|
||||
|
||||
await NadekoBot.Client.SetGameAsync($"SneakyGame event ended.")
|
||||
await NadekoBot.Client.SetGameAsync(GetText("sneakygamestatus_end", cnt))
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@ -112,45 +105,121 @@ namespace NadekoBot.Modules.Gambling
|
||||
.ConfigureAwait(false);
|
||||
|
||||
try { await arg.DeleteAsync(new RequestOptions() { RetryMode = RetryMode.AlwaysFail }).ConfigureAwait(false); }
|
||||
catch { }
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return Task.Delay(0);
|
||||
}
|
||||
|
||||
public static async Task FlowerReactionEvent(CommandContext Context)
|
||||
public async Task FlowerReactionEvent(CommandContext context, int amount)
|
||||
{
|
||||
if (amount <= 0)
|
||||
amount = 100;
|
||||
|
||||
var title = GetText("flowerreaction_title");
|
||||
var desc = GetText("flowerreaction_desc", "🌸", Format.Bold(amount.ToString()) + CurrencySign);
|
||||
var footer = GetText("flowerreaction_footer", 24);
|
||||
var msg = await context.Channel.SendConfirmAsync(title,
|
||||
desc, footer: footer)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
await new FlowerReactionEvent().Start(msg, context, amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class CurrencyEvent
|
||||
{
|
||||
public abstract Task Start(IUserMessage msg, CommandContext channel, int amount);
|
||||
}
|
||||
|
||||
public class FlowerReactionEvent : CurrencyEvent
|
||||
{
|
||||
private readonly ConcurrentHashSet<ulong> _flowerReactionAwardedUsers = new ConcurrentHashSet<ulong>();
|
||||
private readonly Logger _log;
|
||||
|
||||
private IUserMessage msg { get; set; }
|
||||
|
||||
private CancellationTokenSource source { get; }
|
||||
private CancellationToken cancelToken { get; }
|
||||
|
||||
public FlowerReactionEvent()
|
||||
{
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
source = new CancellationTokenSource();
|
||||
cancelToken = source.Token;
|
||||
}
|
||||
|
||||
private async Task End()
|
||||
{
|
||||
if(msg != null)
|
||||
await msg.DeleteAsync().ConfigureAwait(false);
|
||||
|
||||
if(!source.IsCancellationRequested)
|
||||
source.Cancel();
|
||||
|
||||
NadekoBot.Client.MessageDeleted -= MessageDeletedEventHandler;
|
||||
}
|
||||
|
||||
private Task MessageDeletedEventHandler(ulong id, Optional<SocketMessage> _) {
|
||||
if (msg?.Id == id)
|
||||
{
|
||||
_log.Warn("Stopping flower reaction event because message is deleted.");
|
||||
var __ = Task.Run(End);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public override async Task Start(IUserMessage umsg, CommandContext context, int amount)
|
||||
{
|
||||
msg = umsg;
|
||||
NadekoBot.Client.MessageDeleted += MessageDeletedEventHandler;
|
||||
|
||||
try { await msg.AddReactionAsync("🌸").ConfigureAwait(false); }
|
||||
catch
|
||||
{
|
||||
var msg = await Context.Channel.SendConfirmAsync("Flower reaction event started!",
|
||||
"Add 🌸 reaction to this message to get 100" + NadekoBot.BotConfig.CurrencySign,
|
||||
footer: "This event is active for 24 hours.")
|
||||
.ConfigureAwait(false);
|
||||
try { await msg.AddReactionAsync("🌸").ConfigureAwait(false); }
|
||||
catch
|
||||
{
|
||||
try { await msg.AddReactionAsync("🌸").ConfigureAwait(false); }
|
||||
catch
|
||||
try { await msg.DeleteAsync().ConfigureAwait(false); }
|
||||
catch { return; }
|
||||
}
|
||||
}
|
||||
using (msg.OnReaction(async (r) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (r.Emoji.Name == "🌸" && r.User.IsSpecified && ((DateTime.UtcNow - r.User.Value.CreatedAt).TotalDays > 5) && _flowerReactionAwardedUsers.Add(r.User.Value.Id))
|
||||
{
|
||||
try { await msg.DeleteAsync().ConfigureAwait(false); }
|
||||
catch { return; }
|
||||
await CurrencyHandler.AddCurrencyAsync(r.User.Value, "Flower Reaction Event", amount, false)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
using (msg.OnReaction(async (r) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (r.Emoji.Name == "🌸" && r.User.IsSpecified && ((DateTime.UtcNow - r.User.Value.CreatedAt).TotalDays > 5) && _flowerReactionAwardedUsers.Add(r.User.Value.Id))
|
||||
{
|
||||
try { await CurrencyHandler.AddCurrencyAsync(r.User.Value, "Flower Reaction Event", 100, false).ConfigureAwait(false); } catch { }
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}))
|
||||
catch
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromHours(24)).ConfigureAwait(false);
|
||||
try { await msg.DeleteAsync().ConfigureAwait(false); } catch { }
|
||||
_flowerReactionAwardedUsers.Clear();
|
||||
// ignored
|
||||
}
|
||||
}))
|
||||
{
|
||||
try
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromHours(24), cancelToken).ConfigureAwait(false);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
|
||||
}
|
||||
if (cancelToken.IsCancellationRequested)
|
||||
return;
|
||||
|
||||
_log.Warn("Stopping flower reaction event because it expired.");
|
||||
await End();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using ImageSharp.Formats;
|
||||
using Image = ImageSharp.Image;
|
||||
|
||||
namespace NadekoBot.Modules.Gambling
|
||||
@ -17,12 +18,12 @@ namespace NadekoBot.Modules.Gambling
|
||||
public partial class Gambling
|
||||
{
|
||||
[Group]
|
||||
public class DriceRollCommands : ModuleBase
|
||||
public class DriceRollCommands : NadekoSubmodule
|
||||
{
|
||||
private Regex dndRegex { get; } = new Regex(@"^(?<n1>\d+)d(?<n2>\d+)(?:\+(?<add>\d+))?(?:\-(?<sub>\d+))?$", RegexOptions.Compiled);
|
||||
private Regex fudgeRegex { get; } = new Regex(@"^(?<n1>\d+)d(?:F|f)$", RegexOptions.Compiled);
|
||||
|
||||
private readonly char[] fateRolls = new[] { '-', ' ', '+' };
|
||||
private readonly char[] _fateRolls = { '-', ' ', '+' };
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task Roll()
|
||||
@ -34,17 +35,15 @@ namespace NadekoBot.Modules.Gambling
|
||||
var num2 = gen % 10;
|
||||
var imageStream = await Task.Run(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var ms = new MemoryStream();
|
||||
new[] { GetDice(num1), GetDice(num2) }.Merge().SaveAsPng(ms);
|
||||
ms.Position = 0;
|
||||
return ms;
|
||||
}
|
||||
catch { return new MemoryStream(); }
|
||||
});
|
||||
var ms = new MemoryStream();
|
||||
new[] { GetDice(num1), GetDice(num2) }.Merge().Save(ms);
|
||||
ms.Position = 0;
|
||||
return ms;
|
||||
}).ConfigureAwait(false);
|
||||
|
||||
await Context.Channel.SendFileAsync(imageStream, "dice.png", $"{Context.User.Mention} rolled " + Format.Code(gen.ToString())).ConfigureAwait(false);
|
||||
await Context.Channel.SendFileAsync(imageStream,
|
||||
"dice.png",
|
||||
Context.User.Mention + " " + GetText("dice_rolled", Format.Code(gen.ToString()))).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public enum RollOrderType
|
||||
@ -82,11 +81,11 @@ namespace NadekoBot.Modules.Gambling
|
||||
await InternallDndRoll(arg, false).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task InternalRoll( int num, bool ordered)
|
||||
private async Task InternalRoll(int num, bool ordered)
|
||||
{
|
||||
if (num < 1 || num > 30)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("Invalid number specified. You can roll up to 1-30 dice at a time.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("dice_invalid_number", 1, 30).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -122,9 +121,14 @@ namespace NadekoBot.Modules.Gambling
|
||||
|
||||
var bitmap = dice.Merge();
|
||||
var ms = new MemoryStream();
|
||||
bitmap.SaveAsPng(ms);
|
||||
bitmap.Save(ms);
|
||||
ms.Position = 0;
|
||||
await Context.Channel.SendFileAsync(ms, "dice.png", $"{Context.User.Mention} rolled {values.Count} {(values.Count == 1 ? "die" : "dice")}. Total: **{values.Sum()}** Average: **{(values.Sum() / (1.0f * values.Count)).ToString("N2")}**").ConfigureAwait(false);
|
||||
await Context.Channel.SendFileAsync(ms, "dice.png",
|
||||
Context.User.Mention + " " +
|
||||
GetText("dice_rolled_num", Format.Bold(values.Count.ToString())) +
|
||||
" " + GetText("total_average",
|
||||
Format.Bold(values.Sum().ToString()),
|
||||
Format.Bold((values.Sum() / (1.0f * values.Count)).ToString("N2")))).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task InternallDndRoll(string arg, bool ordered)
|
||||
@ -142,9 +146,9 @@ namespace NadekoBot.Modules.Gambling
|
||||
|
||||
for (int i = 0; i < n1; i++)
|
||||
{
|
||||
rolls.Add(fateRolls[rng.Next(0, fateRolls.Length)]);
|
||||
rolls.Add(_fateRolls[rng.Next(0, _fateRolls.Length)]);
|
||||
}
|
||||
var embed = new EmbedBuilder().WithOkColor().WithDescription($"{Context.User.Mention} rolled {n1} fate {(n1 == 1 ? "die" : "dice")}.")
|
||||
var embed = new EmbedBuilder().WithOkColor().WithDescription(Context.User.Mention + " " + GetText("dice_rolled_num", Format.Bold(n1.ToString())))
|
||||
.AddField(efb => efb.WithName(Format.Bold("Result"))
|
||||
.WithValue(string.Join(" ", rolls.Select(c => Format.Code($"[{c}]")))));
|
||||
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
|
||||
@ -168,7 +172,7 @@ namespace NadekoBot.Modules.Gambling
|
||||
}
|
||||
|
||||
var sum = arr.Sum();
|
||||
var embed = new EmbedBuilder().WithOkColor().WithDescription($"{Context.User.Mention} rolled {n1} {(n1 == 1 ? "die" : "dice")} `1 to {n2}`")
|
||||
var embed = new EmbedBuilder().WithOkColor().WithDescription(Context.User.Mention + " " +GetText("dice_rolled_num", n1) + $"`1 - {n2}`")
|
||||
.AddField(efb => efb.WithName(Format.Bold("Rolls"))
|
||||
.WithValue(string.Join(" ", (ordered ? arr.OrderBy(x => x).AsEnumerable() : arr).Select(x => Format.Code(x.ToString())))))
|
||||
.AddField(efb => efb.WithName(Format.Bold("Sum"))
|
||||
@ -181,48 +185,48 @@ namespace NadekoBot.Modules.Gambling
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task NRoll([Remainder] string range)
|
||||
{
|
||||
try
|
||||
int rolled;
|
||||
if (range.Contains("-"))
|
||||
{
|
||||
int rolled;
|
||||
if (range.Contains("-"))
|
||||
var arr = range.Split('-')
|
||||
.Take(2)
|
||||
.Select(int.Parse)
|
||||
.ToArray();
|
||||
if (arr[0] > arr[1])
|
||||
{
|
||||
var arr = range.Split('-')
|
||||
.Take(2)
|
||||
.Select(int.Parse)
|
||||
.ToArray();
|
||||
if (arr[0] > arr[1])
|
||||
throw new ArgumentException("Second argument must be larger than the first one.");
|
||||
rolled = new NadekoRandom().Next(arr[0], arr[1] + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
rolled = new NadekoRandom().Next(0, int.Parse(range) + 1);
|
||||
await ReplyErrorLocalized("second_larger_than_first").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
rolled = new NadekoRandom().Next(arr[0], arr[1] + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
rolled = new NadekoRandom().Next(0, int.Parse(range) + 1);
|
||||
}
|
||||
|
||||
await Context.Channel.SendConfirmAsync($"{Context.User.Mention} rolled **{rolled}**.").ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync($":anger: {ex.Message}").ConfigureAwait(false);
|
||||
}
|
||||
await ReplyConfirmLocalized("dice_rolled", Format.Bold(rolled.ToString())).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private Image GetDice(int num)
|
||||
{
|
||||
const string pathToImage = "data/images/dice";
|
||||
if (num != 10)
|
||||
if (num < 0 || num > 10)
|
||||
throw new ArgumentOutOfRangeException(nameof(num));
|
||||
|
||||
if (num == 10)
|
||||
{
|
||||
using (var stream = File.OpenRead(Path.Combine(pathToImage, $"{num}.png")))
|
||||
return new Image(stream);
|
||||
var images = NadekoBot.Images.Dice;
|
||||
using (var imgOneStream = images[1].Value.ToStream())
|
||||
using (var imgZeroStream = images[0].Value.ToStream())
|
||||
{
|
||||
Image imgOne = new Image(imgOneStream);
|
||||
Image imgZero = new Image(imgZeroStream);
|
||||
|
||||
return new[] { imgOne, imgZero }.Merge();
|
||||
}
|
||||
}
|
||||
|
||||
using (var one = File.OpenRead(Path.Combine(pathToImage, "1.png")))
|
||||
using (var zero = File.OpenRead(Path.Combine(pathToImage, "0.png")))
|
||||
using (var die = NadekoBot.Images.Dice[num].Value.ToStream())
|
||||
{
|
||||
Image imgOne = new Image(one);
|
||||
Image imgZero = new Image(zero);
|
||||
|
||||
return new[] { imgOne, imgZero }.Merge();
|
||||
return new Image(die);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,8 +4,6 @@ using ImageSharp;
|
||||
using NadekoBot.Attributes;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Modules.Gambling.Models;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
@ -17,17 +15,17 @@ namespace NadekoBot.Modules.Gambling
|
||||
public partial class Gambling
|
||||
{
|
||||
[Group]
|
||||
public class DrawCommands : ModuleBase
|
||||
public class DrawCommands : NadekoSubmodule
|
||||
{
|
||||
private static readonly ConcurrentDictionary<IGuild, Cards> AllDecks = new ConcurrentDictionary<IGuild, Cards>();
|
||||
private static readonly ConcurrentDictionary<IGuild, Cards> _allDecks = new ConcurrentDictionary<IGuild, Cards>();
|
||||
|
||||
private const string cardsPath = "data/images/cards";
|
||||
private const string _cardsPath = "data/images/cards";
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task Draw(int num = 1)
|
||||
{
|
||||
var cards = AllDecks.GetOrAdd(Context.Guild, (s) => new Cards());
|
||||
var cards = _allDecks.GetOrAdd(Context.Guild, (s) => new Cards());
|
||||
var images = new List<Image>();
|
||||
var cardObjects = new List<Cards.Card>();
|
||||
if (num > 5) num = 5;
|
||||
@ -35,16 +33,23 @@ namespace NadekoBot.Modules.Gambling
|
||||
{
|
||||
if (cards.CardPool.Count == 0 && i != 0)
|
||||
{
|
||||
try { await Context.Channel.SendErrorAsync("No more cards in a deck.").ConfigureAwait(false); } catch { }
|
||||
try
|
||||
{
|
||||
await ReplyErrorLocalized("no_more_cards").ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
break;
|
||||
}
|
||||
var currentCard = cards.DrawACard();
|
||||
cardObjects.Add(currentCard);
|
||||
using (var stream = File.OpenRead(Path.Combine(cardsPath, currentCard.ToString().ToLowerInvariant()+ ".jpg").Replace(' ','_')))
|
||||
using (var stream = File.OpenRead(Path.Combine(_cardsPath, currentCard.ToString().ToLowerInvariant()+ ".jpg").Replace(' ','_')))
|
||||
images.Add(new Image(stream));
|
||||
}
|
||||
MemoryStream bitmapStream = new MemoryStream();
|
||||
images.Merge().SaveAsPng(bitmapStream);
|
||||
images.Merge().Save(bitmapStream);
|
||||
bitmapStream.Position = 0;
|
||||
var toSend = $"{Context.User.Mention}";
|
||||
if (cardObjects.Count == 5)
|
||||
@ -59,7 +64,7 @@ namespace NadekoBot.Modules.Gambling
|
||||
{
|
||||
//var channel = (ITextChannel)Context.Channel;
|
||||
|
||||
AllDecks.AddOrUpdate(Context.Guild,
|
||||
_allDecks.AddOrUpdate(Context.Guild,
|
||||
(g) => new Cards(),
|
||||
(g, c) =>
|
||||
{
|
||||
@ -67,7 +72,7 @@ namespace NadekoBot.Modules.Gambling
|
||||
return c;
|
||||
});
|
||||
|
||||
await Context.Channel.SendConfirmAsync("Deck reshuffled.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("deck_reshuffled").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,10 @@
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using ImageSharp;
|
||||
using NadekoBot.Attributes;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Services;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Image = ImageSharp.Image;
|
||||
|
||||
@ -14,34 +13,59 @@ namespace NadekoBot.Modules.Gambling
|
||||
public partial class Gambling
|
||||
{
|
||||
[Group]
|
||||
public class FlipCoinCommands : ModuleBase
|
||||
public class FlipCoinCommands : NadekoSubmodule
|
||||
{
|
||||
private readonly IImagesService _images;
|
||||
|
||||
private static NadekoRandom rng { get; } = new NadekoRandom();
|
||||
private const string headsPath = "data/images/coins/heads.png";
|
||||
private const string tailsPath = "data/images/coins/tails.png";
|
||||
|
||||
|
||||
public FlipCoinCommands()
|
||||
{
|
||||
//todo DI in the future, can't atm
|
||||
_images = NadekoBot.Images;
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task Flip(int count = 1)
|
||||
{
|
||||
if (count == 1)
|
||||
{
|
||||
if (rng.Next(0, 2) == 1)
|
||||
await Context.Channel.SendFileAsync(File.Open(headsPath, FileMode.OpenOrCreate), "heads.jpg", $"{Context.User.Mention} flipped " + Format.Code("Heads") + ".").ConfigureAwait(false);
|
||||
{
|
||||
using (var heads = _images.Heads.ToStream())
|
||||
{
|
||||
await Context.Channel.SendFileAsync(heads, "heads.jpg", Context.User.Mention + " " + GetText("flipped", Format.Bold(GetText("heads"))) + ".").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
else
|
||||
await Context.Channel.SendFileAsync(File.Open(tailsPath, FileMode.OpenOrCreate), "tails.jpg", $"{Context.User.Mention} flipped " + Format.Code("Tails") + ".").ConfigureAwait(false);
|
||||
{
|
||||
using (var tails = _images.Tails.ToStream())
|
||||
{
|
||||
await Context.Channel.SendFileAsync(tails, "tails.jpg", Context.User.Mention + " " + GetText("flipped", Format.Bold(GetText("tails"))) + ".").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (count > 10 || count < 1)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("`Invalid number specified. You can flip 1 to 10 coins.`");
|
||||
await ReplyErrorLocalized("flip_invalid", 10).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
var imgs = new Image[count];
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
imgs[i] = rng.Next(0, 10) < 5 ?
|
||||
new Image(File.OpenRead(headsPath)) :
|
||||
new Image(File.OpenRead(tailsPath));
|
||||
using (var heads = _images.Heads.ToStream())
|
||||
using (var tails = _images.Tails.ToStream())
|
||||
{
|
||||
if (rng.Next(0, 10) < 5)
|
||||
{
|
||||
imgs[i] = new Image(heads);
|
||||
}
|
||||
else
|
||||
{
|
||||
imgs[i] = new Image(tails);
|
||||
}
|
||||
}
|
||||
}
|
||||
await Context.Channel.SendFileAsync(imgs.Merge().ToStream(), $"{count} coins.png").ConfigureAwait(false);
|
||||
}
|
||||
@ -55,45 +79,47 @@ namespace NadekoBot.Modules.Gambling
|
||||
|
||||
if (amount < NadekoBot.BotConfig.MinimumBetAmount)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync($"You can't bet less than {NadekoBot.BotConfig.MinimumBetAmount}{CurrencySign}.")
|
||||
.ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("min_bet_limit", NadekoBot.BotConfig.MinimumBetAmount + CurrencySign).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
var removed = await CurrencyHandler.RemoveCurrencyAsync(Context.User, "Betflip Gamble", amount, false).ConfigureAwait(false);
|
||||
if (!removed)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync($"{Context.User.Mention} You don't have enough {CurrencyPluralName}.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("not_enough", CurrencyPluralName).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
//heads = true
|
||||
//tails = false
|
||||
|
||||
//todo this seems stinky, no time to look at it right now
|
||||
var isHeads = guessStr == "HEADS" || guessStr == "H";
|
||||
bool result = false;
|
||||
string imgPathToSend;
|
||||
var result = false;
|
||||
IEnumerable<byte> imageToSend;
|
||||
if (rng.Next(0, 2) == 1)
|
||||
{
|
||||
imgPathToSend = headsPath;
|
||||
imageToSend = _images.Heads;
|
||||
result = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
imgPathToSend = tailsPath;
|
||||
imageToSend = _images.Tails;
|
||||
}
|
||||
|
||||
string str;
|
||||
if (isHeads == result)
|
||||
{
|
||||
var toWin = (int)Math.Round(amount * NadekoBot.BotConfig.BetflipMultiplier);
|
||||
str = $"{Context.User.Mention}`You guessed it!` You won {toWin}{CurrencySign}";
|
||||
str = Context.User.Mention + " " + GetText("flip_guess", toWin + CurrencySign);
|
||||
await CurrencyHandler.AddCurrencyAsync(Context.User, "Betflip Gamble", toWin, false).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
str = $"{Context.User.Mention}`Better luck next time.`";
|
||||
str = Context.User.Mention + " " + GetText("better_luck");
|
||||
}
|
||||
using (var toSend = imageToSend.ToStream())
|
||||
{
|
||||
await Context.Channel.SendFileAsync(toSend, "result.png", str).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await Context.Channel.SendFileAsync(File.Open(imgPathToSend, FileMode.OpenOrCreate), new FileInfo(imgPathToSend).Name, str).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using ImageSharp;
|
||||
using NadekoBot.Attributes;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Services;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
@ -16,62 +16,29 @@ namespace NadekoBot.Modules.Gambling
|
||||
public partial class Gambling
|
||||
{
|
||||
[Group]
|
||||
public class Slots : ModuleBase
|
||||
public class Slots : NadekoSubmodule
|
||||
{
|
||||
private static int totalBet = 0;
|
||||
private static int totalPaidOut = 0;
|
||||
private static int _totalBet;
|
||||
private static int _totalPaidOut;
|
||||
|
||||
private const string backgroundPath = "data/slots/background.png";
|
||||
|
||||
private static readonly byte[] backgroundBuffer;
|
||||
private static readonly byte[][] numbersBuffer = new byte[10][];
|
||||
private static readonly byte[][] emojiBuffer;
|
||||
|
||||
const int alphaCutOut = byte.MaxValue / 3;
|
||||
|
||||
static Slots()
|
||||
{
|
||||
backgroundBuffer = File.ReadAllBytes(backgroundPath);
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
numbersBuffer[i] = File.ReadAllBytes("data/slots/" + i + ".png");
|
||||
}
|
||||
int throwaway;
|
||||
var emojiFiles = Directory.GetFiles("data/slots/emojis/", "*.png")
|
||||
.Where(f => int.TryParse(Path.GetFileNameWithoutExtension(f), out throwaway))
|
||||
.OrderBy(f => int.Parse(Path.GetFileNameWithoutExtension(f)))
|
||||
.ToArray();
|
||||
|
||||
emojiBuffer = new byte[emojiFiles.Length][];
|
||||
for (int i = 0; i < emojiFiles.Length; i++)
|
||||
{
|
||||
emojiBuffer[i] = File.ReadAllBytes(emojiFiles[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static MemoryStream InternalGetStream(string path)
|
||||
{
|
||||
var ms = new MemoryStream();
|
||||
using (var fs = File.Open(path, FileMode.Open))
|
||||
{
|
||||
fs.CopyTo(ms);
|
||||
fs.Flush();
|
||||
}
|
||||
ms.Position = 0;
|
||||
return ms;
|
||||
}
|
||||
private const int _alphaCutOut = byte.MaxValue / 3;
|
||||
|
||||
//here is a payout chart
|
||||
//https://lh6.googleusercontent.com/-i1hjAJy_kN4/UswKxmhrbPI/AAAAAAAAB1U/82wq_4ZZc-Y/DE6B0895-6FC1-48BE-AC4F-14D1B91AB75B.jpg
|
||||
//thanks to judge for helping me with this
|
||||
|
||||
private readonly IImagesService _images;
|
||||
|
||||
public Slots()
|
||||
{
|
||||
_images = NadekoBot.Images;
|
||||
}
|
||||
|
||||
public class SlotMachine
|
||||
{
|
||||
public const int MaxValue = 5;
|
||||
|
||||
static readonly List<Func<int[], int>> winningCombos = new List<Func<int[], int>>()
|
||||
static readonly List<Func<int[], int>> _winningCombos = new List<Func<int[], int>>()
|
||||
{
|
||||
//three flowers
|
||||
(arr) => arr.All(a=>a==MaxValue) ? 30 : 0,
|
||||
@ -86,14 +53,14 @@ namespace NadekoBot.Modules.Gambling
|
||||
public static SlotResult Pull()
|
||||
{
|
||||
var numbers = new int[3];
|
||||
for (int i = 0; i < numbers.Length; i++)
|
||||
for (var i = 0; i < numbers.Length; i++)
|
||||
{
|
||||
numbers[i] = new NadekoRandom().Next(0, MaxValue + 1);
|
||||
}
|
||||
int multi = 0;
|
||||
for (int i = 0; i < winningCombos.Count; i++)
|
||||
var multi = 0;
|
||||
foreach (var t in _winningCombos)
|
||||
{
|
||||
multi = winningCombos[i](numbers);
|
||||
multi = t(numbers);
|
||||
if (multi != 0)
|
||||
break;
|
||||
}
|
||||
@ -107,8 +74,8 @@ namespace NadekoBot.Modules.Gambling
|
||||
public int Multiplier { get; }
|
||||
public SlotResult(int[] nums, int multi)
|
||||
{
|
||||
this.Numbers = nums;
|
||||
this.Multiplier = multi;
|
||||
Numbers = nums;
|
||||
Multiplier = multi;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -118,8 +85,8 @@ namespace NadekoBot.Modules.Gambling
|
||||
public async Task SlotStats()
|
||||
{
|
||||
//i remembered to not be a moron
|
||||
var paid = totalPaidOut;
|
||||
var bet = totalBet;
|
||||
var paid = _totalPaidOut;
|
||||
var bet = _totalBet;
|
||||
|
||||
if (bet <= 0)
|
||||
bet = 1;
|
||||
@ -154,7 +121,7 @@ namespace NadekoBot.Modules.Gambling
|
||||
var sb = new StringBuilder();
|
||||
const int bet = 1;
|
||||
int payout = 0;
|
||||
foreach (var key in dict.Keys.OrderByDescending(x=>x))
|
||||
foreach (var key in dict.Keys.OrderByDescending(x => x))
|
||||
{
|
||||
sb.AppendLine($"x{key} occured {dict[key]} times. {dict[key] * 1.0f / tests * 100}%");
|
||||
payout += key * dict[key];
|
||||
@ -163,142 +130,104 @@ namespace NadekoBot.Modules.Gambling
|
||||
footer: $"Total Bet: {tests * bet} | Payout: {payout * bet} | {payout * 1.0f / tests * 100}%");
|
||||
}
|
||||
|
||||
static HashSet<ulong> runningUsers = new HashSet<ulong>();
|
||||
private static readonly HashSet<ulong> _runningUsers = new HashSet<ulong>();
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task Slot(int amount = 0)
|
||||
{
|
||||
if (!runningUsers.Add(Context.User.Id))
|
||||
if (!_runningUsers.Add(Context.User.Id))
|
||||
return;
|
||||
try
|
||||
{
|
||||
if (amount < 1)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync($"You can't bet less than 1{NadekoBot.BotConfig.CurrencySign}").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("min_bet_limit", 1 + CurrencySign).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (amount > 999)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync($"You can't bet more than 999{NadekoBot.BotConfig.CurrencySign}").ConfigureAwait(false);
|
||||
GetText("slot_maxbet", 999 + CurrencySign);
|
||||
await ReplyErrorLocalized("max_bet_limit", 999 + CurrencySign).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!await CurrencyHandler.RemoveCurrencyAsync(Context.User, "Slot Machine", amount, false))
|
||||
{
|
||||
await Context.Channel.SendErrorAsync($"You don't have enough {NadekoBot.BotConfig.CurrencySign}.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("not_enough", CurrencySign).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
Interlocked.Add(ref totalBet, amount);
|
||||
using (var bgFileStream = new MemoryStream(backgroundBuffer))
|
||||
Interlocked.Add(ref _totalBet, amount);
|
||||
using (var bgFileStream = NadekoBot.Images.SlotBackground.ToStream())
|
||||
{
|
||||
var bgImage = new ImageSharp.Image(bgFileStream);
|
||||
|
||||
var result = SlotMachine.Pull();
|
||||
int[] numbers = result.Numbers;
|
||||
using (var bgPixels = bgImage.Lock())
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
for (int i = 0; i < 3; i++)
|
||||
using (var file = _images.SlotEmojis[numbers[i]].ToStream())
|
||||
using (var randomImage = new ImageSharp.Image(file))
|
||||
{
|
||||
using (var file = new MemoryStream(emojiBuffer[numbers[i]]))
|
||||
{
|
||||
var randomImage = new ImageSharp.Image(file);
|
||||
using (var toAdd = randomImage.Lock())
|
||||
{
|
||||
for (int j = 0; j < toAdd.Width; j++)
|
||||
{
|
||||
for (int k = 0; k < toAdd.Height; k++)
|
||||
{
|
||||
var x = 95 + 142 * i + j;
|
||||
int y = 330 + k;
|
||||
var toSet = toAdd[j, k];
|
||||
if (toSet.A < alphaCutOut)
|
||||
continue;
|
||||
bgPixels[x, y] = toAdd[j, k];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
bgImage.DrawImage(randomImage, 100, default(Size), new Point(95 + 142 * i, 330));
|
||||
}
|
||||
|
||||
var won = amount * result.Multiplier;
|
||||
var printWon = won;
|
||||
var n = 0;
|
||||
do
|
||||
{
|
||||
var digit = printWon % 10;
|
||||
using (var fs = new MemoryStream(numbersBuffer[digit]))
|
||||
{
|
||||
var img = new ImageSharp.Image(fs);
|
||||
using (var pixels = img.Lock())
|
||||
{
|
||||
for (int i = 0; i < pixels.Width; i++)
|
||||
{
|
||||
for (int j = 0; j < pixels.Height; j++)
|
||||
{
|
||||
if (pixels[i, j].A < alphaCutOut)
|
||||
continue;
|
||||
var x = 230 - n * 16 + i;
|
||||
bgPixels[x, 462 + j] = pixels[i, j];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
n++;
|
||||
} while ((printWon /= 10) != 0);
|
||||
|
||||
var printAmount = amount;
|
||||
n = 0;
|
||||
do
|
||||
{
|
||||
var digit = printAmount % 10;
|
||||
using (var fs = new MemoryStream(numbersBuffer[digit]))
|
||||
{
|
||||
var img = new ImageSharp.Image(fs);
|
||||
using (var pixels = img.Lock())
|
||||
{
|
||||
for (int i = 0; i < pixels.Width; i++)
|
||||
{
|
||||
for (int j = 0; j < pixels.Height; j++)
|
||||
{
|
||||
if (pixels[i, j].A < alphaCutOut)
|
||||
continue;
|
||||
var x = 395 - n * 16 + i;
|
||||
bgPixels[x, 462 + j] = pixels[i, j];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
n++;
|
||||
} while ((printAmount /= 10) != 0);
|
||||
}
|
||||
|
||||
var msg = "Better luck next time ^_^";
|
||||
var won = amount * result.Multiplier;
|
||||
var printWon = won;
|
||||
var n = 0;
|
||||
do
|
||||
{
|
||||
var digit = printWon % 10;
|
||||
using (var fs = NadekoBot.Images.SlotNumbers[digit].ToStream())
|
||||
using (var img = new ImageSharp.Image(fs))
|
||||
{
|
||||
bgImage.DrawImage(img, 100, default(Size), new Point(230 - n * 16, 462));
|
||||
}
|
||||
n++;
|
||||
} while ((printWon /= 10) != 0);
|
||||
|
||||
var printAmount = amount;
|
||||
n = 0;
|
||||
do
|
||||
{
|
||||
var digit = printAmount % 10;
|
||||
using (var fs = _images.SlotNumbers[digit].ToStream())
|
||||
using (var img = new ImageSharp.Image(fs))
|
||||
{
|
||||
bgImage.DrawImage(img, 100, default(Size), new Point(395 - n * 16, 462));
|
||||
}
|
||||
n++;
|
||||
} while ((printAmount /= 10) != 0);
|
||||
|
||||
var msg = GetText("better_luck");
|
||||
if (result.Multiplier != 0)
|
||||
{
|
||||
await CurrencyHandler.AddCurrencyAsync(Context.User, $"Slot Machine x{result.Multiplier}", amount * result.Multiplier, false);
|
||||
Interlocked.Add(ref totalPaidOut, amount * result.Multiplier);
|
||||
Interlocked.Add(ref _totalPaidOut, amount * result.Multiplier);
|
||||
if (result.Multiplier == 1)
|
||||
msg = $"A single {NadekoBot.BotConfig.CurrencySign}, x1 - Try again!";
|
||||
msg = GetText("slot_single", CurrencySign, 1);
|
||||
else if (result.Multiplier == 4)
|
||||
msg = $"Good job! Two {NadekoBot.BotConfig.CurrencySign} - bet x4";
|
||||
msg = GetText("slot_two", CurrencySign, 4);
|
||||
else if (result.Multiplier == 10)
|
||||
msg = "Wow! Lucky! Three of a kind! x10";
|
||||
msg = GetText("slot_three", 10);
|
||||
else if (result.Multiplier == 30)
|
||||
msg = "WOAAHHHHHH!!! Congratulations!!! x30";
|
||||
msg = GetText("slot_jackpot", 30);
|
||||
}
|
||||
|
||||
await Context.Channel.SendFileAsync(bgImage.ToStream(), "result.png", Context.User.Mention + " " + msg + $"\n`Bet:`{amount} `Won:` {amount * result.Multiplier}{NadekoBot.BotConfig.CurrencySign}").ConfigureAwait(false);
|
||||
await Context.Channel.SendFileAsync(bgImage.ToStream(), "result.png", Context.User.Mention + " " + msg + $"\n`{GetText("slot_bet")}:`{amount} `{GetText("slot_won")}:` {amount * result.Multiplier}{NadekoBot.BotConfig.CurrencySign}").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
var t = Task.Run(async () =>
|
||||
var _ = Task.Run(async () =>
|
||||
{
|
||||
await Task.Delay(3000);
|
||||
runningUsers.Remove(Context.User.Id);
|
||||
await Task.Delay(2000);
|
||||
_runningUsers.Remove(Context.User.Id);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -3,13 +3,11 @@ using Discord.Commands;
|
||||
using NadekoBot.Attributes;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Services;
|
||||
using NadekoBot.Services.Database;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NadekoBot.Modules.Gambling
|
||||
@ -47,10 +45,10 @@ namespace NadekoBot.Modules.Gambling
|
||||
}
|
||||
|
||||
[Group]
|
||||
public class WaifuClaimCommands : ModuleBase
|
||||
public class WaifuClaimCommands : NadekoSubmodule
|
||||
{
|
||||
private static ConcurrentDictionary<ulong, DateTime> _divorceCooldowns { get; } = new ConcurrentDictionary<ulong, DateTime>();
|
||||
private static ConcurrentDictionary<ulong, DateTime> _affinityCooldowns { get; } = new ConcurrentDictionary<ulong, DateTime>();
|
||||
private static ConcurrentDictionary<ulong, DateTime> divorceCooldowns { get; } = new ConcurrentDictionary<ulong, DateTime>();
|
||||
private static ConcurrentDictionary<ulong, DateTime> affinityCooldowns { get; } = new ConcurrentDictionary<ulong, DateTime>();
|
||||
|
||||
enum WaifuClaimResult
|
||||
{
|
||||
@ -65,20 +63,19 @@ namespace NadekoBot.Modules.Gambling
|
||||
{
|
||||
if (amount < 50)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync($"{Context.User.Mention} No waifu is that cheap. You must pay at least 50{NadekoBot.BotConfig.CurrencySign} to get a waifu, even if their actual value is lower.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("waifu_isnt_cheap", 50 + CurrencySign).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (target.Id == Context.User.Id)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync(Context.User.Mention + " You can't claim yourself.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("waifu_not_yourself").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
WaifuClaimResult result = WaifuClaimResult.NotEnoughFunds;
|
||||
int? oldPrice = null;
|
||||
WaifuClaimResult result;
|
||||
WaifuInfo w;
|
||||
var isAffinity = false;
|
||||
bool isAffinity;
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
{
|
||||
w = uow.Waifus.ByWaifuUserId(target.Id);
|
||||
@ -110,7 +107,7 @@ namespace NadekoBot.Modules.Gambling
|
||||
result = WaifuClaimResult.Success;
|
||||
}
|
||||
}
|
||||
else if (isAffinity && amount >= w.Price * 0.88f)
|
||||
else if (isAffinity && amount > w.Price * 0.88f)
|
||||
{
|
||||
if (!await CurrencyHandler.RemoveCurrencyAsync(Context.User.Id, "Claimed Waifu", amount, uow).ConfigureAwait(false))
|
||||
{
|
||||
@ -120,7 +117,6 @@ namespace NadekoBot.Modules.Gambling
|
||||
{
|
||||
var oldClaimer = w.Claimer;
|
||||
w.Claimer = uow.DiscordUsers.GetOrCreate(Context.User);
|
||||
oldPrice = w.Price;
|
||||
w.Price = amount + (amount / 4);
|
||||
result = WaifuClaimResult.Success;
|
||||
|
||||
@ -143,7 +139,6 @@ namespace NadekoBot.Modules.Gambling
|
||||
{
|
||||
var oldClaimer = w.Claimer;
|
||||
w.Claimer = uow.DiscordUsers.GetOrCreate(Context.User);
|
||||
oldPrice = w.Price;
|
||||
w.Price = amount;
|
||||
result = WaifuClaimResult.Success;
|
||||
|
||||
@ -165,22 +160,20 @@ namespace NadekoBot.Modules.Gambling
|
||||
|
||||
if (result == WaifuClaimResult.InsufficientAmount)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync($"{Context.User.Mention} You must pay {Math.Ceiling(w.Price * (isAffinity ? 0.88f : 1.1f))} or more to claim that waifu!").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("waifu_not_enough", Math.Ceiling(w.Price * (isAffinity ? 0.88f : 1.1f))).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
else if (result == WaifuClaimResult.NotEnoughFunds)
|
||||
if (result == WaifuClaimResult.NotEnoughFunds)
|
||||
{
|
||||
await Context.Channel.SendConfirmAsync($"{Context.User.Mention} you don't have {amount}{NadekoBot.BotConfig.CurrencySign}!")
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
var msg = $"{Context.User.Mention} claimed {target.Mention} as their waifu for {amount}{NadekoBot.BotConfig.CurrencySign}!";
|
||||
if (w.Affinity?.UserId == Context.User.Id)
|
||||
msg += $"\n🎉 Their love is fulfilled! 🎉\n**{target}'s** new value is {w.Price}{NadekoBot.BotConfig.CurrencySign}!";
|
||||
await Context.Channel.SendConfirmAsync(msg)
|
||||
.ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("not_enough", CurrencySign).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
var msg = GetText("waifu_claimed",
|
||||
Format.Bold(target.ToString()),
|
||||
amount + CurrencySign);
|
||||
if (w.Affinity?.UserId == Context.User.Id)
|
||||
msg += "\n" + GetText("waifu_fulfilled", target, w.Price + CurrencySign);
|
||||
await Context.Channel.SendConfirmAsync(Context.User.Mention + msg).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public enum DivorceResult
|
||||
@ -192,29 +185,27 @@ namespace NadekoBot.Modules.Gambling
|
||||
}
|
||||
|
||||
|
||||
private static readonly TimeSpan DivorceLimit = TimeSpan.FromHours(6);
|
||||
private static readonly TimeSpan _divorceLimit = TimeSpan.FromHours(6);
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task Divorce([Remainder]IUser target)
|
||||
{
|
||||
var channel = (ITextChannel)Context.Channel;
|
||||
|
||||
if (target.Id == Context.User.Id)
|
||||
return;
|
||||
|
||||
var result = DivorceResult.NotYourWife;
|
||||
TimeSpan difference = TimeSpan.Zero;
|
||||
DivorceResult result;
|
||||
var difference = TimeSpan.Zero;
|
||||
var amount = 0;
|
||||
WaifuInfo w = null;
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
{
|
||||
w = uow.Waifus.ByWaifuUserId(target.Id);
|
||||
var now = DateTime.UtcNow;
|
||||
if (w == null || w.Claimer == null || w.Claimer.UserId != Context.User.Id)
|
||||
if (w?.Claimer == null || w.Claimer.UserId != Context.User.Id)
|
||||
result = DivorceResult.NotYourWife;
|
||||
else if (_divorceCooldowns.AddOrUpdate(Context.User.Id,
|
||||
else if (divorceCooldowns.AddOrUpdate(Context.User.Id,
|
||||
now,
|
||||
(key, old) => ((difference = now.Subtract(old)) > DivorceLimit) ? now : old) != now)
|
||||
(key, old) => ((difference = now.Subtract(old)) > _divorceLimit) ? now : old) != now)
|
||||
{
|
||||
result = DivorceResult.Cooldown;
|
||||
}
|
||||
@ -251,37 +242,39 @@ namespace NadekoBot.Modules.Gambling
|
||||
|
||||
if (result == DivorceResult.SucessWithPenalty)
|
||||
{
|
||||
await Context.Channel.SendConfirmAsync($"{Context.User.Mention} You have divorced a waifu who likes you. You heartless monster.\n{w.Waifu} received {amount}{NadekoBot.BotConfig.CurrencySign} as a compensation.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("waifu_divorced_like", Format.Bold(w.Waifu.ToString()), amount + CurrencySign).ConfigureAwait(false);
|
||||
}
|
||||
else if (result == DivorceResult.Success)
|
||||
{
|
||||
await Context.Channel.SendConfirmAsync($"{Context.User.Mention} You have divorced a waifu who doesn't like you. You received {amount}{NadekoBot.BotConfig.CurrencySign} back.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("waifu_divorced_notlike", amount + CurrencySign).ConfigureAwait(false);
|
||||
}
|
||||
else if (result == DivorceResult.NotYourWife)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync($"{Context.User.Mention} That waifu is not yours.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("waifu_not_yours").ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
var remaining = DivorceLimit.Subtract(difference);
|
||||
await Context.Channel.SendErrorAsync($"{Context.User.Mention} You divorced recently. You must wait **{remaining.Hours} hours and {remaining.Minutes} minutes** to divorce again.").ConfigureAwait(false);
|
||||
var remaining = _divorceLimit.Subtract(difference);
|
||||
await ReplyErrorLocalized("waifu_recent_divorce",
|
||||
Format.Bold(((int)remaining.TotalHours).ToString()),
|
||||
Format.Bold(remaining.Minutes.ToString())).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly TimeSpan AffinityLimit = TimeSpan.FromMinutes(30);
|
||||
private static readonly TimeSpan _affinityLimit = TimeSpan.FromMinutes(30);
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task WaifuClaimerAffinity([Remainder]IUser u = null)
|
||||
{
|
||||
if (u?.Id == Context.User.Id)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync($"{Context.User.Mention} you can't set affinity to yourself, you egomaniac.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("waifu_egomaniac").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
DiscordUser oldAff = null;
|
||||
var sucess = false;
|
||||
var cooldown = false;
|
||||
TimeSpan difference = TimeSpan.Zero;
|
||||
var difference = TimeSpan.Zero;
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
{
|
||||
var w = uow.Waifus.ByWaifuUserId(Context.User.Id);
|
||||
@ -289,13 +282,11 @@ namespace NadekoBot.Modules.Gambling
|
||||
var now = DateTime.UtcNow;
|
||||
if (w?.Affinity?.UserId == u?.Id)
|
||||
{
|
||||
sucess = false;
|
||||
}
|
||||
else if (_affinityCooldowns.AddOrUpdate(Context.User.Id,
|
||||
else if (affinityCooldowns.AddOrUpdate(Context.User.Id,
|
||||
now,
|
||||
(key, old) => ((difference = now.Subtract(old)) > AffinityLimit) ? now : old) != now)
|
||||
(key, old) => ((difference = now.Subtract(old)) > _affinityLimit) ? now : old) != now)
|
||||
{
|
||||
sucess = false;
|
||||
cooldown = true;
|
||||
}
|
||||
else if (w == null)
|
||||
@ -340,19 +331,29 @@ namespace NadekoBot.Modules.Gambling
|
||||
{
|
||||
if (cooldown)
|
||||
{
|
||||
var remaining = AffinityLimit.Subtract(difference);
|
||||
await Context.Channel.SendErrorAsync($"{Context.User.Mention} You must wait **{remaining.Hours} hours and {remaining.Minutes} minutes** in order to change your affinity again.").ConfigureAwait(false);
|
||||
var remaining = _affinityLimit.Subtract(difference);
|
||||
await ReplyErrorLocalized("waifu_affinity_cooldown",
|
||||
Format.Bold(((int)remaining.TotalHours).ToString()),
|
||||
Format.Bold(remaining.Minutes.ToString())).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
await Context.Channel.SendErrorAsync($"{Context.User.Mention} your affinity is already set to that waifu or you're trying to remove your affinity while not having one.").ConfigureAwait(false);
|
||||
{
|
||||
await ReplyErrorLocalized("waifu_affinity_already").ConfigureAwait(false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (u == null)
|
||||
await Context.Channel.SendConfirmAsync("Affinity Reset", $"{Context.User.Mention} Your affinity is reset. You no longer have a person you like.").ConfigureAwait(false);
|
||||
{
|
||||
await ReplyConfirmLocalized("waifu_affinity_reset").ConfigureAwait(false);
|
||||
}
|
||||
else if (oldAff == null)
|
||||
await Context.Channel.SendConfirmAsync("Affinity Set", $"{Context.User.Mention} wants to be {u.Mention}'s waifu. Aww <3").ConfigureAwait(false);
|
||||
{
|
||||
await ReplyConfirmLocalized("waifu_affinity_set", Format.Bold(u.ToString())).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
await Context.Channel.SendConfirmAsync("Affinity Changed", $"{Context.User.Mention} changed their affinity from {oldAff} to {u.Mention}.\n\n*This is morally questionable.*🤔").ConfigureAwait(false);
|
||||
{
|
||||
await ReplyConfirmLocalized("waifu_affinity_changed", Format.Bold(oldAff.ToString()), Format.Bold(u.ToString())).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -367,19 +368,20 @@ namespace NadekoBot.Modules.Gambling
|
||||
|
||||
if (waifus.Count == 0)
|
||||
{
|
||||
await Context.Channel.SendConfirmAsync("No waifus have been claimed yet.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("waifus_none").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
var embed = new EmbedBuilder()
|
||||
.WithTitle("Top Waifus")
|
||||
.WithTitle(GetText("waifus_top_waifus"))
|
||||
.WithOkColor();
|
||||
|
||||
for (int i = 0; i < waifus.Count; i++)
|
||||
for (var i = 0; i < waifus.Count; i++)
|
||||
{
|
||||
var w = waifus[i];
|
||||
|
||||
embed.AddField(efb => efb.WithName("#" + (i + 1) + " - " + w.Price + NadekoBot.BotConfig.CurrencySign).WithValue(w.ToString()).WithIsInline(false));
|
||||
var j = i;
|
||||
embed.AddField(efb => efb.WithName("#" + (j + 1) + " - " + w.Price + NadekoBot.BotConfig.CurrencySign).WithValue(w.ToString()).WithIsInline(false));
|
||||
}
|
||||
|
||||
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
|
||||
@ -403,6 +405,7 @@ namespace NadekoBot.Modules.Gambling
|
||||
x.UpdateType == WaifuUpdateType.Claimed &&
|
||||
x.New == null);
|
||||
if (w == null)
|
||||
{
|
||||
uow.Waifus.Add(w = new WaifuInfo()
|
||||
{
|
||||
Affinity = null,
|
||||
@ -410,21 +413,28 @@ namespace NadekoBot.Modules.Gambling
|
||||
Price = 1,
|
||||
Waifu = uow.DiscordUsers.GetOrCreate(target),
|
||||
});
|
||||
}
|
||||
|
||||
w.Waifu.Username = target.Username;
|
||||
w.Waifu.Discriminator = target.Discriminator;
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
var claimInfo = GetClaimTitle(target.Id);
|
||||
var affInfo = GetAffinityTitle(target.Id);
|
||||
|
||||
var rng = new NadekoRandom();
|
||||
|
||||
var nobody = GetText("nobody");
|
||||
var embed = new EmbedBuilder()
|
||||
.WithOkColor()
|
||||
.WithTitle("Waifu " + w.Waifu.ToString() + " - \"the " + claimInfo.Title + "\"")
|
||||
.AddField(efb => efb.WithName("Price").WithValue(w.Price.ToString()).WithIsInline(true))
|
||||
.AddField(efb => efb.WithName("Claimed by").WithValue(w.Claimer?.ToString() ?? "No one").WithIsInline(true))
|
||||
.AddField(efb => efb.WithName("Likes").WithValue(w.Affinity?.ToString() ?? "Nobody").WithIsInline(true))
|
||||
.AddField(efb => efb.WithName("Changes Of Heart").WithValue($"{affInfo.Count} - \"the {affInfo.Title}\"").WithIsInline(true))
|
||||
.AddField(efb => efb.WithName("Divorces").WithValue(divorces.ToString()).WithIsInline(true))
|
||||
.AddField(efb => efb.WithName($"Waifus ({claims.Count})").WithValue(claims.Count == 0 ? "Nobody" : string.Join("\n", claims.Select(x => x.Waifu))).WithIsInline(true));
|
||||
.WithTitle("Waifu " + w.Waifu + " - \"the " + claimInfo.Title + "\"")
|
||||
.AddField(efb => efb.WithName(GetText("price")).WithValue(w.Price.ToString()).WithIsInline(true))
|
||||
.AddField(efb => efb.WithName(GetText("claimed_by")).WithValue(w.Claimer?.ToString() ?? nobody).WithIsInline(true))
|
||||
.AddField(efb => efb.WithName(GetText("likes")).WithValue(w.Affinity?.ToString() ?? nobody).WithIsInline(true))
|
||||
.AddField(efb => efb.WithName(GetText("changes_of_heart")).WithValue($"{affInfo.Count} - \"the {affInfo.Title}\"").WithIsInline(true))
|
||||
.AddField(efb => efb.WithName(GetText("divorces")).WithValue(divorces.ToString()).WithIsInline(true))
|
||||
.AddField(efb => efb.WithName($"Waifus ({claims.Count})").WithValue(claims.Count == 0 ? nobody : string.Join("\n", claims.OrderBy(x => rng.Next()).Take(40).Select(x => x.Waifu))).WithIsInline(true));
|
||||
|
||||
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
|
||||
}
|
||||
@ -444,13 +454,13 @@ namespace NadekoBot.Modules.Gambling
|
||||
|
||||
private static WaifuProfileTitle GetClaimTitle(ulong userId)
|
||||
{
|
||||
int count = 0;
|
||||
int count;
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
{
|
||||
count = uow.Waifus.ByClaimerUserId(userId).Count;
|
||||
}
|
||||
|
||||
ClaimTitles title = ClaimTitles.Lonely;
|
||||
ClaimTitles title;
|
||||
if (count == 0)
|
||||
title = ClaimTitles.Lonely;
|
||||
else if (count == 1)
|
||||
@ -481,13 +491,13 @@ namespace NadekoBot.Modules.Gambling
|
||||
|
||||
private static WaifuProfileTitle GetAffinityTitle(ulong userId)
|
||||
{
|
||||
int count = 0;
|
||||
int count;
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
{
|
||||
count = uow._context.WaifuUpdates.Count(w => w.User.UserId == userId && w.UpdateType == WaifuUpdateType.AffinityChanged);
|
||||
}
|
||||
|
||||
AffinityTitles title = AffinityTitles.Pure;
|
||||
AffinityTitles title;
|
||||
if (count < 1)
|
||||
title = AffinityTitles.Pure;
|
||||
else if (count < 2)
|
||||
|
@ -1,9 +1,9 @@
|
||||
using Discord;
|
||||
using System;
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using NadekoBot.Attributes;
|
||||
using NadekoBot.Extensions;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using NadekoBot.Services;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
@ -12,7 +12,7 @@ using System.Collections.Generic;
|
||||
namespace NadekoBot.Modules.Gambling
|
||||
{
|
||||
[NadekoModule("Gambling", "$")]
|
||||
public partial class Gambling : DiscordModule
|
||||
public partial class Gambling : NadekoTopLevelModule
|
||||
{
|
||||
public static string CurrencyName { get; set; }
|
||||
public static string CurrencyPluralName { get; set; }
|
||||
@ -42,23 +42,24 @@ namespace NadekoBot.Modules.Gambling
|
||||
var members = role.Members().Where(u => u.Status != UserStatus.Offline && u.Status != UserStatus.Unknown);
|
||||
var membersArray = members as IUser[] ?? members.ToArray();
|
||||
var usr = membersArray[new NadekoRandom().Next(0, membersArray.Length)];
|
||||
await Context.Channel.SendConfirmAsync("🎟 Raffled user", $"**{usr.Username}#{usr.Discriminator}**", footer: $"ID: {usr.Id}").ConfigureAwait(false);
|
||||
await Context.Channel.SendConfirmAsync("🎟 "+ GetText("raffled_user"), $"**{usr.Username}#{usr.Discriminator}**", footer: $"ID: {usr.Id}").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[Priority(0)]
|
||||
public async Task Cash([Remainder] IUser user = null)
|
||||
{
|
||||
user = user ?? Context.User;
|
||||
|
||||
await Context.Channel.SendConfirmAsync($"{user.Username} has {GetCurrency(user.Id)} {CurrencySign}").ConfigureAwait(false);
|
||||
if(user == null)
|
||||
await ConfirmLocalized("has", Format.Bold(Context.User.ToString()), $"{GetCurrency(Context.User.Id)} {CurrencySign}").ConfigureAwait(false);
|
||||
else
|
||||
await ReplyConfirmLocalized("has", Format.Bold(user.ToString()), $"{GetCurrency(user.Id)} {CurrencySign}").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[Priority(1)]
|
||||
public async Task Cash(ulong userId)
|
||||
{
|
||||
await Context.Channel.SendConfirmAsync($"`{userId}` has {GetCurrency(userId)} {CurrencySign}").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("has", Format.Code(userId.ToString()), $"{GetCurrency(userId)} {CurrencySign}").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -70,11 +71,12 @@ namespace NadekoBot.Modules.Gambling
|
||||
var success = await CurrencyHandler.RemoveCurrencyAsync((IGuildUser)Context.User, $"Gift to {receiver.Username} ({receiver.Id}).", amount, false).ConfigureAwait(false);
|
||||
if (!success)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync($"{Context.User.Mention} You don't have enough {CurrencyPluralName}.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("not_enough", CurrencyPluralName).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
await CurrencyHandler.AddCurrencyAsync(receiver, $"Gift from {Context.User.Username} ({Context.User.Id}).", amount, true).ConfigureAwait(false);
|
||||
await Context.Channel.SendConfirmAsync($"{Context.User.Mention} gifted {amount}{CurrencySign} to {Format.Bold(receiver.ToString())}!").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("gifted", amount + CurrencySign, Format.Bold(receiver.ToString()))
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -93,8 +95,7 @@ namespace NadekoBot.Modules.Gambling
|
||||
return;
|
||||
|
||||
await CurrencyHandler.AddCurrencyAsync(usrId, $"Awarded by bot owner. ({Context.User.Username}/{Context.User.Id})", amount).ConfigureAwait(false);
|
||||
|
||||
await Context.Channel.SendConfirmAsync($"{Context.User.Mention} awarded {amount}{CurrencySign} to <@{usrId}>!").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("awarded", amount + CurrencySign, $"<@{usrId}>").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -103,7 +104,6 @@ namespace NadekoBot.Modules.Gambling
|
||||
[Priority(0)]
|
||||
public async Task Award(int amount, [Remainder] IRole role)
|
||||
{
|
||||
var channel = (ITextChannel)Context.Channel;
|
||||
var users = (await Context.Guild.GetUsersAsync())
|
||||
.Where(u => u.GetRoles().Contains(role))
|
||||
.ToList();
|
||||
@ -112,9 +112,10 @@ namespace NadekoBot.Modules.Gambling
|
||||
amount)))
|
||||
.ConfigureAwait(false);
|
||||
|
||||
await Context.Channel.SendConfirmAsync($"Awarded `{amount}` {CurrencyPluralName} to `{users.Count}` users from `{role.Name}` role.")
|
||||
.ConfigureAwait(false);
|
||||
|
||||
await ReplyConfirmLocalized("mass_award",
|
||||
amount + CurrencySign,
|
||||
Format.Bold(users.Count.ToString()),
|
||||
Format.Bold(role.Name)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -126,9 +127,9 @@ namespace NadekoBot.Modules.Gambling
|
||||
return;
|
||||
|
||||
if (await CurrencyHandler.RemoveCurrencyAsync(user, $"Taken by bot owner.({Context.User.Username}/{Context.User.Id})", amount, true).ConfigureAwait(false))
|
||||
await Context.Channel.SendConfirmAsync($"{Context.User.Mention} successfully took {amount} {(amount == 1 ? CurrencyName : CurrencyPluralName)} from {user}!").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("take", amount+CurrencySign, Format.Bold(user.ToString())).ConfigureAwait(false);
|
||||
else
|
||||
await Context.Channel.SendErrorAsync($"{Context.User.Mention} was unable to take {amount} {(amount == 1 ? CurrencyName : CurrencyPluralName)} from {user} because the user doesn't have that much {CurrencyPluralName}!").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("take_fail", amount + CurrencySign, Format.Bold(user.ToString()), CurrencyPluralName).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
|
||||
@ -140,9 +141,9 @@ namespace NadekoBot.Modules.Gambling
|
||||
return;
|
||||
|
||||
if (await CurrencyHandler.RemoveCurrencyAsync(usrId, $"Taken by bot owner.({Context.User.Username}/{Context.User.Id})", amount).ConfigureAwait(false))
|
||||
await Context.Channel.SendConfirmAsync($"{Context.User.Mention} successfully took {amount} {(amount == 1 ? CurrencyName : CurrencyPluralName)} from <@{usrId}>!").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("take", amount + CurrencySign, $"<@{usrId}>").ConfigureAwait(false);
|
||||
else
|
||||
await Context.Channel.SendErrorAsync($"{Context.User.Mention} was unable to take {amount} {(amount == 1 ? CurrencyName : CurrencyPluralName)} from `{usrId}` because the user doesn't have that much {CurrencyPluralName}!").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("take_fail", amount + CurrencySign, Format.Code(usrId.ToString()), CurrencyPluralName).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
//[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -206,49 +207,46 @@ namespace NadekoBot.Modules.Gambling
|
||||
if (amount < 1)
|
||||
return;
|
||||
|
||||
long userFlowers;
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
if (!await CurrencyHandler.RemoveCurrencyAsync(Context.User, "Betroll Gamble", amount, false).ConfigureAwait(false))
|
||||
{
|
||||
userFlowers = uow.Currency.GetOrCreate(Context.User.Id).Amount;
|
||||
}
|
||||
|
||||
if (userFlowers < amount)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync($"{Context.User.Mention} You don't have enough {CurrencyPluralName}. You only have {userFlowers}{CurrencySign}.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("not_enough", CurrencyPluralName).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
await CurrencyHandler.RemoveCurrencyAsync(Context.User, "Betroll Gamble", amount, false).ConfigureAwait(false);
|
||||
|
||||
var rng = new NadekoRandom().Next(0, 101);
|
||||
var str = $"{Context.User.Mention} `You rolled {rng}.` ";
|
||||
if (rng < 67)
|
||||
var rnd = new NadekoRandom().Next(0, 101);
|
||||
var str = Context.User.Mention + Format.Code(GetText("roll", rnd));
|
||||
if (rnd < 67)
|
||||
{
|
||||
str += "Better luck next time.";
|
||||
}
|
||||
else if (rng < 91)
|
||||
{
|
||||
str += $"Congratulations! You won {amount * NadekoBot.BotConfig.Betroll67Multiplier}{CurrencySign} for rolling above 66";
|
||||
await CurrencyHandler.AddCurrencyAsync(Context.User, "Betroll Gamble", (int)(amount * NadekoBot.BotConfig.Betroll67Multiplier), false).ConfigureAwait(false);
|
||||
}
|
||||
else if (rng < 100)
|
||||
{
|
||||
str += $"Congratulations! You won {amount * NadekoBot.BotConfig.Betroll91Multiplier}{CurrencySign} for rolling above 90.";
|
||||
await CurrencyHandler.AddCurrencyAsync(Context.User, "Betroll Gamble", (int)(amount * NadekoBot.BotConfig.Betroll91Multiplier), false).ConfigureAwait(false);
|
||||
str += GetText("better_luck");
|
||||
}
|
||||
else
|
||||
{
|
||||
str += $"👑 Congratulations! You won {amount * NadekoBot.BotConfig.Betroll100Multiplier}{CurrencySign} for rolling **100**. 👑";
|
||||
await CurrencyHandler.AddCurrencyAsync(Context.User, "Betroll Gamble", (int)(amount * NadekoBot.BotConfig.Betroll100Multiplier), false).ConfigureAwait(false);
|
||||
if (rnd < 91)
|
||||
{
|
||||
str += GetText("br_win", (amount * NadekoBot.BotConfig.Betroll67Multiplier) + CurrencySign, 66);
|
||||
await CurrencyHandler.AddCurrencyAsync(Context.User, "Betroll Gamble",
|
||||
(int) (amount * NadekoBot.BotConfig.Betroll67Multiplier), false).ConfigureAwait(false);
|
||||
}
|
||||
else if (rnd < 100)
|
||||
{
|
||||
str += GetText("br_win", (amount * NadekoBot.BotConfig.Betroll91Multiplier) + CurrencySign, 90);
|
||||
await CurrencyHandler.AddCurrencyAsync(Context.User, "Betroll Gamble",
|
||||
(int) (amount * NadekoBot.BotConfig.Betroll91Multiplier), false).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
str += GetText("br_win", (amount * NadekoBot.BotConfig.Betroll100Multiplier) + CurrencySign, 100) + " 👑";
|
||||
await CurrencyHandler.AddCurrencyAsync(Context.User, "Betroll Gamble",
|
||||
(int) (amount * NadekoBot.BotConfig.Betroll100Multiplier), false).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
await Context.Channel.SendConfirmAsync(str).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task Leaderboard()
|
||||
{
|
||||
var richest = new List<Currency>();
|
||||
List<Currency> richest;
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
{
|
||||
richest = uow.Currency.GetTopRichest(9).ToList();
|
||||
@ -256,22 +254,22 @@ namespace NadekoBot.Modules.Gambling
|
||||
if (!richest.Any())
|
||||
return;
|
||||
|
||||
|
||||
var embed = new EmbedBuilder()
|
||||
.WithOkColor()
|
||||
.WithTitle(NadekoBot.BotConfig.CurrencySign + " Leaderboard");
|
||||
.WithTitle(NadekoBot.BotConfig.CurrencySign + " " + GetText("leaderboard"));
|
||||
|
||||
for (var i = 0; i < richest.Count; i++)
|
||||
{
|
||||
var x = richest[i];
|
||||
var usr = await Context.Guild.GetUserAsync(x.UserId).ConfigureAwait(false);
|
||||
var usrStr = "";
|
||||
if (usr == null)
|
||||
usrStr = x.UserId.ToString();
|
||||
else
|
||||
usrStr = usr.Username?.TrimTo(20, true);
|
||||
var usrStr = usr == null
|
||||
? x.UserId.ToString()
|
||||
: usr.Username?.TrimTo(20, true);
|
||||
|
||||
embed.AddField(efb => efb.WithName("#" + (i + 1) + " " + usrStr).WithValue(x.Amount.ToString() + " " + NadekoBot.BotConfig.CurrencySign).WithIsInline(true));
|
||||
var j = i;
|
||||
embed.AddField(efb => efb.WithName("#" + (j + 1) + " " + usrStr)
|
||||
.WithValue(x.Amount.ToString() + " " + NadekoBot.BotConfig.CurrencySign)
|
||||
.WithIsInline(true));
|
||||
}
|
||||
|
||||
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
|
||||
|
@ -19,7 +19,7 @@ namespace NadekoBot.Modules.Games
|
||||
public partial class Games
|
||||
{
|
||||
[Group]
|
||||
public class Acropobia : ModuleBase
|
||||
public class Acropobia : NadekoSubmodule
|
||||
{
|
||||
//channelId, game
|
||||
public static ConcurrentDictionary<ulong, AcrophobiaGame> AcrophobiaGames { get; } = new ConcurrentDictionary<ulong, AcrophobiaGame>();
|
||||
@ -28,6 +28,8 @@ namespace NadekoBot.Modules.Games
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task Acro(int time = 60)
|
||||
{
|
||||
if (time < 10 || time > 120)
|
||||
return;
|
||||
var channel = (ITextChannel)Context.Channel;
|
||||
|
||||
var game = new AcrophobiaGame(channel, time);
|
||||
@ -45,7 +47,7 @@ namespace NadekoBot.Modules.Games
|
||||
}
|
||||
else
|
||||
{
|
||||
await channel.SendErrorAsync("Acrophobia game is already running in this channel.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("acro_running").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -59,44 +61,44 @@ namespace NadekoBot.Modules.Games
|
||||
|
||||
public class AcrophobiaGame
|
||||
{
|
||||
private readonly ITextChannel channel;
|
||||
private readonly int time;
|
||||
private readonly NadekoRandom rng;
|
||||
private readonly ImmutableArray<char> startingLetters;
|
||||
private readonly CancellationTokenSource source;
|
||||
private readonly ITextChannel _channel;
|
||||
private readonly int _time;
|
||||
private readonly NadekoRandom _rng;
|
||||
private readonly ImmutableArray<char> _startingLetters;
|
||||
private readonly CancellationTokenSource _source;
|
||||
private AcroPhase phase { get; set; } = AcroPhase.Submitting;
|
||||
|
||||
private readonly ConcurrentDictionary<string, IGuildUser> submissions = new ConcurrentDictionary<string, IGuildUser>();
|
||||
public IReadOnlyDictionary<string, IGuildUser> Submissions => submissions;
|
||||
private readonly ConcurrentDictionary<string, IGuildUser> _submissions = new ConcurrentDictionary<string, IGuildUser>();
|
||||
public IReadOnlyDictionary<string, IGuildUser> Submissions => _submissions;
|
||||
|
||||
private readonly ConcurrentHashSet<ulong> usersWhoSubmitted = new ConcurrentHashSet<ulong>();
|
||||
private readonly ConcurrentHashSet<ulong> usersWhoVoted = new ConcurrentHashSet<ulong>();
|
||||
private readonly ConcurrentHashSet<ulong> _usersWhoSubmitted = new ConcurrentHashSet<ulong>();
|
||||
private readonly ConcurrentHashSet<ulong> _usersWhoVoted = new ConcurrentHashSet<ulong>();
|
||||
|
||||
private int spamCount = 0;
|
||||
private int _spamCount;
|
||||
|
||||
//text, votes
|
||||
private readonly ConcurrentDictionary<string, int> votes = new ConcurrentDictionary<string, int>();
|
||||
private readonly ConcurrentDictionary<string, int> _votes = new ConcurrentDictionary<string, int>();
|
||||
private readonly Logger _log;
|
||||
|
||||
public AcrophobiaGame(ITextChannel channel, int time)
|
||||
{
|
||||
this._log = LogManager.GetCurrentClassLogger();
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
this.channel = channel;
|
||||
this.time = time;
|
||||
this.source = new CancellationTokenSource();
|
||||
_channel = channel;
|
||||
_time = time;
|
||||
_source = new CancellationTokenSource();
|
||||
|
||||
this.rng = new NadekoRandom();
|
||||
var wordCount = rng.Next(3, 6);
|
||||
_rng = new NadekoRandom();
|
||||
var wordCount = _rng.Next(3, 6);
|
||||
|
||||
var lettersArr = new char[wordCount];
|
||||
|
||||
for (int i = 0; i < wordCount; i++)
|
||||
{
|
||||
var randChar = (char)rng.Next(65, 91);
|
||||
lettersArr[i] = randChar == 'X' ? (char)rng.Next(65, 88) : randChar;
|
||||
var randChar = (char)_rng.Next(65, 91);
|
||||
lettersArr[i] = randChar == 'X' ? (char)_rng.Next(65, 88) : randChar;
|
||||
}
|
||||
startingLetters = lettersArr.ToImmutableArray();
|
||||
_startingLetters = lettersArr.ToImmutableArray();
|
||||
}
|
||||
|
||||
private EmbedBuilder GetEmbed()
|
||||
@ -104,19 +106,19 @@ namespace NadekoBot.Modules.Games
|
||||
var i = 0;
|
||||
return phase == AcroPhase.Submitting
|
||||
|
||||
? new EmbedBuilder().WithOkColor()
|
||||
.WithTitle("Acrophobia")
|
||||
.WithDescription($"Game started. Create a sentence with the following acronym: **{string.Join(".", startingLetters)}.**\n")
|
||||
.WithFooter(efb => efb.WithText("You have " + this.time + " seconds to make a submission."))
|
||||
? new EmbedBuilder().WithOkColor()
|
||||
.WithTitle(GetText("acrophobia"))
|
||||
.WithDescription(GetText("acro_started", Format.Bold(string.Join(".", _startingLetters))))
|
||||
.WithFooter(efb => efb.WithText(GetText("acro_started_footer", _time)))
|
||||
|
||||
: new EmbedBuilder()
|
||||
.WithOkColor()
|
||||
.WithTitle("Acrophobia - Submissions Closed")
|
||||
.WithDescription($@"Acronym was **{string.Join(".", startingLetters)}.**
|
||||
--
|
||||
{this.submissions.Aggregate("", (agg, cur) => agg + $"`{++i}.` **{cur.Key.ToLowerInvariant().ToTitleCase()}**\n")}
|
||||
--")
|
||||
.WithFooter(efb => efb.WithText("Vote by typing a number of the submission"));
|
||||
: new EmbedBuilder()
|
||||
.WithOkColor()
|
||||
.WithTitle(GetText("acrophobia") + " - " + GetText("submissions_closed"))
|
||||
.WithDescription(GetText("acro_nym_was", Format.Bold(string.Join(".", _startingLetters)) + "\n" +
|
||||
$@"--
|
||||
{_submissions.Aggregate("",(agg, cur) => agg + $"`{++i}.` **{cur.Key.ToLowerInvariant().ToTitleCase()}**\n")}
|
||||
--"))
|
||||
.WithFooter(efb => efb.WithText(GetText("acro_vote")));
|
||||
}
|
||||
|
||||
public async Task Run()
|
||||
@ -125,10 +127,10 @@ namespace NadekoBot.Modules.Games
|
||||
var embed = GetEmbed();
|
||||
|
||||
//SUBMISSIONS PHASE
|
||||
await channel.EmbedAsync(embed).ConfigureAwait(false);
|
||||
await _channel.EmbedAsync(embed).ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
await Task.Delay(time * 1000, source.Token).ConfigureAwait(false);
|
||||
await Task.Delay(_time * 1000, _source.Token).ConfigureAwait(false);
|
||||
phase = AcroPhase.Idle;
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
@ -137,30 +139,32 @@ namespace NadekoBot.Modules.Games
|
||||
}
|
||||
|
||||
//var i = 0;
|
||||
if (submissions.Count == 0)
|
||||
if (_submissions.Count == 0)
|
||||
{
|
||||
await channel.SendErrorAsync("Acrophobia", "Game ended with no submissions.");
|
||||
await _channel.SendErrorAsync(GetText("acrophobia"), GetText("acro_ended_no_sub"));
|
||||
return;
|
||||
}
|
||||
else if (submissions.Count == 1)
|
||||
if (_submissions.Count == 1)
|
||||
{
|
||||
await channel.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||
.WithDescription($"{submissions.First().Value.Mention} is the winner for being the only user who made a submission!")
|
||||
.WithFooter(efb => efb.WithText(submissions.First().Key.ToLowerInvariant().ToTitleCase())))
|
||||
.ConfigureAwait(false);
|
||||
await _channel.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||
.WithDescription(
|
||||
GetText("acro_winner_only",
|
||||
Format.Bold(_submissions.First().Value.ToString())))
|
||||
.WithFooter(efb => efb.WithText(_submissions.First().Key.ToLowerInvariant().ToTitleCase())))
|
||||
.ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
var submissionClosedEmbed = GetEmbed();
|
||||
|
||||
await channel.EmbedAsync(submissionClosedEmbed).ConfigureAwait(false);
|
||||
await _channel.EmbedAsync(submissionClosedEmbed).ConfigureAwait(false);
|
||||
|
||||
//VOTING PHASE
|
||||
this.phase = AcroPhase.Voting;
|
||||
phase = AcroPhase.Voting;
|
||||
try
|
||||
{
|
||||
//30 secondds for voting
|
||||
await Task.Delay(30000, source.Token).ConfigureAwait(false);
|
||||
this.phase = AcroPhase.Idle;
|
||||
await Task.Delay(30000, _source.Token).ConfigureAwait(false);
|
||||
phase = AcroPhase.Idle;
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
@ -174,10 +178,10 @@ namespace NadekoBot.Modules.Games
|
||||
try
|
||||
{
|
||||
var msg = arg as SocketUserMessage;
|
||||
if (msg == null || msg.Author.IsBot || msg.Channel.Id != channel.Id)
|
||||
if (msg == null || msg.Author.IsBot || msg.Channel.Id != _channel.Id)
|
||||
return;
|
||||
|
||||
++spamCount;
|
||||
++_spamCount;
|
||||
|
||||
var guildUser = (IGuildUser)msg.Author;
|
||||
|
||||
@ -185,37 +189,39 @@ namespace NadekoBot.Modules.Games
|
||||
|
||||
if (phase == AcroPhase.Submitting)
|
||||
{
|
||||
if (spamCount > 10)
|
||||
if (_spamCount > 10)
|
||||
{
|
||||
spamCount = 0;
|
||||
try { await channel.EmbedAsync(GetEmbed()).ConfigureAwait(false); }
|
||||
_spamCount = 0;
|
||||
try { await _channel.EmbedAsync(GetEmbed()).ConfigureAwait(false); }
|
||||
catch { }
|
||||
}
|
||||
var inputWords = input.Split(' '); //get all words
|
||||
|
||||
if (inputWords.Length != startingLetters.Length) // number of words must be the same as the number of the starting letters
|
||||
if (inputWords.Length != _startingLetters.Length) // number of words must be the same as the number of the starting letters
|
||||
return;
|
||||
|
||||
for (int i = 0; i < startingLetters.Length; i++)
|
||||
for (int i = 0; i < _startingLetters.Length; i++)
|
||||
{
|
||||
var letter = startingLetters[i];
|
||||
var letter = _startingLetters[i];
|
||||
|
||||
if (!inputWords[i].StartsWith(letter.ToString())) // all first letters must match
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (!usersWhoSubmitted.Add(guildUser.Id))
|
||||
if (!_usersWhoSubmitted.Add(guildUser.Id))
|
||||
return;
|
||||
//try adding it to the list of answers
|
||||
if (!submissions.TryAdd(input, guildUser))
|
||||
if (!_submissions.TryAdd(input, guildUser))
|
||||
{
|
||||
usersWhoSubmitted.TryRemove(guildUser.Id);
|
||||
_usersWhoSubmitted.TryRemove(guildUser.Id);
|
||||
return;
|
||||
}
|
||||
|
||||
// all good. valid input. answer recorded
|
||||
await channel.SendConfirmAsync("Acrophobia", $"{guildUser.Mention} submitted their sentence. ({submissions.Count} total)");
|
||||
await _channel.SendConfirmAsync(GetText("acrophobia"),
|
||||
GetText("acro_submit", guildUser.Mention,
|
||||
_submissions.Count));
|
||||
try
|
||||
{
|
||||
await msg.DeleteAsync();
|
||||
@ -227,14 +233,13 @@ namespace NadekoBot.Modules.Games
|
||||
}
|
||||
else if (phase == AcroPhase.Voting)
|
||||
{
|
||||
if (spamCount > 10)
|
||||
if (_spamCount > 10)
|
||||
{
|
||||
spamCount = 0;
|
||||
try { await channel.EmbedAsync(GetEmbed()).ConfigureAwait(false); }
|
||||
_spamCount = 0;
|
||||
try { await _channel.EmbedAsync(GetEmbed()).ConfigureAwait(false); }
|
||||
catch { }
|
||||
}
|
||||
|
||||
IGuildUser usr;
|
||||
//if (submissions.TryGetValue(input, out usr) && usr.Id != guildUser.Id)
|
||||
//{
|
||||
// if (!usersWhoVoted.Add(guildUser.Id))
|
||||
@ -246,17 +251,17 @@ namespace NadekoBot.Modules.Games
|
||||
//}
|
||||
|
||||
int num;
|
||||
if (int.TryParse(input, out num) && num > 0 && num <= submissions.Count)
|
||||
if (int.TryParse(input, out num) && num > 0 && num <= _submissions.Count)
|
||||
{
|
||||
var kvp = submissions.Skip(num - 1).First();
|
||||
usr = kvp.Value;
|
||||
var kvp = _submissions.Skip(num - 1).First();
|
||||
var usr = kvp.Value;
|
||||
//can't vote for yourself, can't vote multiple times
|
||||
if (usr.Id == guildUser.Id || !usersWhoVoted.Add(guildUser.Id))
|
||||
if (usr.Id == guildUser.Id || !_usersWhoVoted.Add(guildUser.Id))
|
||||
return;
|
||||
votes.AddOrUpdate(kvp.Key, 1, (key, old) => ++old);
|
||||
await channel.SendConfirmAsync("Acrophobia", $"{guildUser.Mention} cast their vote!").ConfigureAwait(false);
|
||||
_votes.AddOrUpdate(kvp.Key, 1, (key, old) => ++old);
|
||||
await _channel.SendConfirmAsync(GetText("acrophobia"),
|
||||
GetText("acro_vote_cast", Format.Bold(guildUser.ToString()))).ConfigureAwait(false);
|
||||
await msg.DeleteAsync().ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
@ -269,27 +274,34 @@ namespace NadekoBot.Modules.Games
|
||||
|
||||
public async Task End()
|
||||
{
|
||||
if (!votes.Any())
|
||||
if (!_votes.Any())
|
||||
{
|
||||
await channel.SendErrorAsync("Acrophobia", "No votes cast. Game ended with no winner.").ConfigureAwait(false);
|
||||
await _channel.SendErrorAsync(GetText("acrophobia"), GetText("acro_no_votes_cast")).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
var table = votes.OrderByDescending(v => v.Value);
|
||||
var table = _votes.OrderByDescending(v => v.Value);
|
||||
var winner = table.First();
|
||||
var embed = new EmbedBuilder().WithOkColor()
|
||||
.WithTitle("Acrophobia")
|
||||
.WithDescription($"Winner is {submissions[winner.Key].Mention} with {winner.Value} points.\n")
|
||||
.WithTitle(GetText("acrophobia"))
|
||||
.WithDescription(GetText("acro_winner", Format.Bold(_submissions[winner.Key].ToString()),
|
||||
Format.Bold(winner.Value.ToString())))
|
||||
.WithFooter(efb => efb.WithText(winner.Key.ToLowerInvariant().ToTitleCase()));
|
||||
|
||||
await channel.EmbedAsync(embed).ConfigureAwait(false);
|
||||
await _channel.EmbedAsync(embed).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public void EnsureStopped()
|
||||
{
|
||||
NadekoBot.Client.MessageReceived -= PotentialAcro;
|
||||
if (!source.IsCancellationRequested)
|
||||
source.Cancel();
|
||||
if (!_source.IsCancellationRequested)
|
||||
_source.Cancel();
|
||||
}
|
||||
|
||||
private string GetText(string key, params object[] replacements)
|
||||
=> GetTextStatic(key,
|
||||
NadekoBot.Localization.GetCultureInfo(_channel.Guild),
|
||||
typeof(Games).Name.ToLowerInvariant(),
|
||||
replacements);
|
||||
}
|
||||
}
|
||||
}
|
@ -17,31 +17,23 @@ namespace NadekoBot.Modules.Games
|
||||
public partial class Games
|
||||
{
|
||||
[Group]
|
||||
public class CleverBotCommands : ModuleBase
|
||||
public class CleverBotCommands : NadekoSubmodule
|
||||
{
|
||||
private static Logger _log { get; }
|
||||
private new static Logger _log { get; }
|
||||
|
||||
class CleverAnswer
|
||||
{
|
||||
public string Status { get; set; }
|
||||
public string Response { get; set; }
|
||||
}
|
||||
|
||||
public static ConcurrentDictionary<ulong, Lazy<ChatterBotSession>> CleverbotGuilds { get; } = new ConcurrentDictionary<ulong, Lazy<ChatterBotSession>>();
|
||||
public static ConcurrentDictionary<ulong, Lazy<ChatterBotSession>> CleverbotGuilds { get; }
|
||||
|
||||
static CleverBotCommands()
|
||||
{
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
var sw = Stopwatch.StartNew();
|
||||
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
{
|
||||
var bot = ChatterBotFactory.Create(ChatterBotType.CLEVERBOT);
|
||||
CleverbotGuilds = new ConcurrentDictionary<ulong, Lazy<ChatterBotSession>>(
|
||||
NadekoBot.AllGuildConfigs
|
||||
.Where(gc => gc.CleverbotEnabled)
|
||||
.ToDictionary(gc => gc.GuildId, gc => new Lazy<ChatterBotSession>(() => bot.CreateSession(), true)));
|
||||
}
|
||||
|
||||
var bot = ChatterBotFactory.Create(ChatterBotType.CLEVERBOT);
|
||||
CleverbotGuilds = new ConcurrentDictionary<ulong, Lazy<ChatterBotSession>>(
|
||||
NadekoBot.AllGuildConfigs
|
||||
.Where(gc => gc.CleverbotEnabled)
|
||||
.ToDictionary(gc => gc.GuildId, gc => new Lazy<ChatterBotSession>(() => bot.CreateSession(), true)));
|
||||
|
||||
sw.Stop();
|
||||
_log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s");
|
||||
@ -104,7 +96,7 @@ namespace NadekoBot.Modules.Games
|
||||
uow.GuildConfigs.SetCleverbotEnabled(Context.Guild.Id, false);
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
await Context.Channel.SendConfirmAsync($"{Context.User.Mention} Disabled cleverbot on this server.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("cleverbot_disabled").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -118,7 +110,7 @@ namespace NadekoBot.Modules.Games
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await Context.Channel.SendConfirmAsync($"{Context.User.Mention} Enabled cleverbot on this server.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("cleverbot_enabled").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,8 +9,9 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using NadekoBot.Modules.Games.Commands.Hangman;
|
||||
|
||||
namespace NadekoBot.Modules.Games.Commands.Hangman
|
||||
namespace NadekoBot.Modules.Games.Hangman
|
||||
{
|
||||
public class HangmanTermPool
|
||||
{
|
||||
@ -63,15 +64,14 @@ namespace NadekoBot.Modules.Games.Commands.Hangman
|
||||
public uint MaxErrors { get; } = 6;
|
||||
public uint MessagesSinceLastPost { get; private set; } = 0;
|
||||
public string ScrambledWord => "`" + String.Concat(Term.Word.Select(c =>
|
||||
{
|
||||
if (!(char.IsLetter(c) || char.IsDigit(c)))
|
||||
{
|
||||
if (c == ' ')
|
||||
return " \u2000";
|
||||
if (!(char.IsLetter(c) || char.IsDigit(c)))
|
||||
return $" {c}";
|
||||
|
||||
c = char.ToUpperInvariant(c);
|
||||
|
||||
if (c == ' ')
|
||||
return " ";
|
||||
return Guesses.Contains(c) ? $" {c}" : " _";
|
||||
return Guesses.Contains(c) ? $" {c}" : " ◯";
|
||||
})) + "`";
|
||||
|
||||
public bool GuessedAll => Guesses.IsSupersetOf(Term.Word.ToUpperInvariant()
|
||||
@ -146,7 +146,7 @@ namespace NadekoBot.Modules.Games.Commands.Hangman
|
||||
MessagesSinceLastPost = 0;
|
||||
++Errors;
|
||||
if (Errors < MaxErrors)
|
||||
await GameChannel.SendErrorAsync("Hangman Game", $"{msg.Author.Mention} Letter `{guess}` has already been used.\n" + ScrambledWord + "\n" + GetHangman(),
|
||||
await GameChannel.SendErrorAsync("Hangman Game", $"{msg.Author} Letter `{guess}` has already been used.\n" + ScrambledWord + "\n" + GetHangman(),
|
||||
footer: string.Join(" ", Guesses)).ConfigureAwait(false);
|
||||
else
|
||||
await End().ConfigureAwait(false);
|
||||
@ -159,7 +159,7 @@ namespace NadekoBot.Modules.Games.Commands.Hangman
|
||||
{
|
||||
if (GuessedAll)
|
||||
{
|
||||
try { await GameChannel.SendConfirmAsync("Hangman Game", $"{msg.Author.Mention} guessed a letter `{guess}`!").ConfigureAwait(false); } catch { }
|
||||
try { await GameChannel.SendConfirmAsync("Hangman Game", $"{msg.Author} guessed a letter `{guess}`!").ConfigureAwait(false); } catch { }
|
||||
|
||||
await End().ConfigureAwait(false);
|
||||
return;
|
||||
@ -167,7 +167,7 @@ namespace NadekoBot.Modules.Games.Commands.Hangman
|
||||
MessagesSinceLastPost = 0;
|
||||
try
|
||||
{
|
||||
await GameChannel.SendConfirmAsync("Hangman Game", $"{msg.Author.Mention} guessed a letter `{guess}`!\n" + ScrambledWord + "\n" + GetHangman(),
|
||||
await GameChannel.SendConfirmAsync("Hangman Game", $"{msg.Author} guessed a letter `{guess}`!\n" + ScrambledWord + "\n" + GetHangman(),
|
||||
footer: string.Join(" ", Guesses)).ConfigureAwait(false);
|
||||
}
|
||||
catch { }
|
||||
@ -178,7 +178,7 @@ namespace NadekoBot.Modules.Games.Commands.Hangman
|
||||
MessagesSinceLastPost = 0;
|
||||
++Errors;
|
||||
if (Errors < MaxErrors)
|
||||
await GameChannel.SendErrorAsync("Hangman Game", $"{msg.Author.Mention} Letter `{guess}` does not exist.\n" + ScrambledWord + "\n" + GetHangman(),
|
||||
await GameChannel.SendErrorAsync("Hangman Game", $"{msg.Author} Letter `{guess}` does not exist.\n" + ScrambledWord + "\n" + GetHangman(),
|
||||
footer: string.Join(" ", Guesses)).ConfigureAwait(false);
|
||||
else
|
||||
await End().ConfigureAwait(false);
|
||||
|
@ -1,35 +1,25 @@
|
||||
using Discord.Commands;
|
||||
using NadekoBot.Attributes;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Modules.Games.Commands.Hangman;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading.Tasks;
|
||||
using NadekoBot.Modules.Games.Hangman;
|
||||
using Discord;
|
||||
|
||||
namespace NadekoBot.Modules.Games
|
||||
{
|
||||
public partial class Games
|
||||
{
|
||||
[Group]
|
||||
public class HangmanCommands : ModuleBase
|
||||
public class HangmanCommands : NadekoSubmodule
|
||||
{
|
||||
private static Logger _log { get; }
|
||||
|
||||
//channelId, game
|
||||
public static ConcurrentDictionary<ulong, HangmanGame> HangmanGames { get; } = new ConcurrentDictionary<ulong, HangmanGame>();
|
||||
private static string typesStr { get; } = "";
|
||||
|
||||
static HangmanCommands()
|
||||
{
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
typesStr = $"`List of \"{NadekoBot.ModulePrefixes[typeof(Games).Name]}hangman\" term types:`\n" + String.Join(", ", HangmanTermPool.data.Keys);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task Hangmanlist()
|
||||
{
|
||||
await Context.Channel.SendConfirmAsync(typesStr);
|
||||
await Context.Channel.SendConfirmAsync(Format.Code(GetText("hangman_types", Prefix)) + "\n" + string.Join(", ", HangmanTermPool.data.Keys));
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -39,11 +29,11 @@ namespace NadekoBot.Modules.Games
|
||||
|
||||
if (!HangmanGames.TryAdd(Context.Channel.Id, hm))
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("Hangman game already running on this channel.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("hangman_running").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
hm.OnEnded += (g) =>
|
||||
hm.OnEnded += g =>
|
||||
{
|
||||
HangmanGame throwaway;
|
||||
HangmanGames.TryRemove(g.GameChannel.Id, out throwaway);
|
||||
@ -54,14 +44,14 @@ namespace NadekoBot.Modules.Games
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
try { await Context.Channel.SendErrorAsync($"Starting errored: {ex.Message}").ConfigureAwait(false); } catch { }
|
||||
try { await Context.Channel.SendErrorAsync(GetText("hangman_start_errored") + " " + ex.Message).ConfigureAwait(false); } catch { }
|
||||
HangmanGame throwaway;
|
||||
HangmanGames.TryRemove(Context.Channel.Id, out throwaway);
|
||||
throwaway.Dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
await Context.Channel.SendConfirmAsync("Hangman game started", hm.ScrambledWord + "\n" + hm.GetHangman());
|
||||
await Context.Channel.SendConfirmAsync(GetText("hangman_game_started"), hm.ScrambledWord + "\n" + hm.GetHangman());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,10 +10,8 @@ using NLog;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NadekoBot.Modules.Games
|
||||
@ -28,21 +26,16 @@ namespace NadekoBot.Modules.Games
|
||||
/// https://discord.gg/0TYNJfCU4De7YIk8
|
||||
/// </summary>
|
||||
[Group]
|
||||
public class PlantPickCommands : ModuleBase
|
||||
public class PlantPickCommands : NadekoSubmodule
|
||||
{
|
||||
private static ConcurrentHashSet<ulong> generationChannels { get; } = new ConcurrentHashSet<ulong>();
|
||||
private static ConcurrentHashSet<ulong> generationChannels { get; }
|
||||
//channelid/message
|
||||
private static ConcurrentDictionary<ulong, List<IUserMessage>> plantedFlowers { get; } = new ConcurrentDictionary<ulong, List<IUserMessage>>();
|
||||
//channelId/last generation
|
||||
private static ConcurrentDictionary<ulong, DateTime> lastGenerations { get; } = new ConcurrentDictionary<ulong, DateTime>();
|
||||
|
||||
private static ConcurrentHashSet<ulong> usersRecentlyPicked { get; } = new ConcurrentHashSet<ulong>();
|
||||
|
||||
private static Logger _log { get; }
|
||||
|
||||
static PlantPickCommands()
|
||||
{
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
#if !GLOBAL_NADEKO
|
||||
NadekoBot.Client.MessageReceived += PotentialFlowerGeneration;
|
||||
@ -51,64 +44,74 @@ namespace NadekoBot.Modules.Games
|
||||
.SelectMany(c => c.GenerateCurrencyChannelIds.Select(obj => obj.ChannelId)));
|
||||
}
|
||||
|
||||
private static async Task PotentialFlowerGeneration(SocketMessage imsg)
|
||||
private static Task PotentialFlowerGeneration(SocketMessage imsg)
|
||||
{
|
||||
try
|
||||
var msg = imsg as SocketUserMessage;
|
||||
if (msg == null || msg.IsAuthor() || msg.Author.IsBot)
|
||||
return Task.CompletedTask;
|
||||
|
||||
var channel = imsg.Channel as ITextChannel;
|
||||
if (channel == null)
|
||||
return Task.CompletedTask;
|
||||
|
||||
if (!generationChannels.Contains(channel.Id))
|
||||
return Task.CompletedTask;
|
||||
|
||||
var _ = Task.Run(async () =>
|
||||
{
|
||||
var msg = imsg as SocketUserMessage;
|
||||
if (msg == null || msg.IsAuthor() || msg.Author.IsBot)
|
||||
return;
|
||||
|
||||
var channel = imsg.Channel as ITextChannel;
|
||||
if (channel == null)
|
||||
return;
|
||||
|
||||
if (!generationChannels.Contains(channel.Id))
|
||||
return;
|
||||
|
||||
var lastGeneration = lastGenerations.GetOrAdd(channel.Id, DateTime.MinValue);
|
||||
var rng = new NadekoRandom();
|
||||
|
||||
if (DateTime.Now - TimeSpan.FromSeconds(NadekoBot.BotConfig.CurrencyGenerationCooldown) < lastGeneration) //recently generated in this channel, don't generate again
|
||||
return;
|
||||
|
||||
var num = rng.Next(1, 101) + NadekoBot.BotConfig.CurrencyGenerationChance * 100;
|
||||
|
||||
if (num > 100)
|
||||
try
|
||||
{
|
||||
lastGenerations.AddOrUpdate(channel.Id, DateTime.Now, (id, old) => DateTime.Now);
|
||||
var lastGeneration = lastGenerations.GetOrAdd(channel.Id, DateTime.MinValue);
|
||||
var rng = new NadekoRandom();
|
||||
|
||||
var dropAmount = NadekoBot.BotConfig.CurrencyDropAmount;
|
||||
//todo i'm stupid :rofl: wtg kwoth. real async programming :100: :ok_hand: :100: :100: :thumbsup:
|
||||
if (DateTime.Now - TimeSpan.FromSeconds(NadekoBot.BotConfig.CurrencyGenerationCooldown) < lastGeneration) //recently generated in this channel, don't generate again
|
||||
return;
|
||||
|
||||
if (dropAmount > 0)
|
||||
var num = rng.Next(1, 101) + NadekoBot.BotConfig.CurrencyGenerationChance * 100;
|
||||
|
||||
if (num > 100)
|
||||
{
|
||||
var msgs = new IUserMessage[dropAmount];
|
||||
lastGenerations.AddOrUpdate(channel.Id, DateTime.Now, (id, old) => DateTime.Now);
|
||||
|
||||
string firstPart;
|
||||
if (dropAmount == 1)
|
||||
var dropAmount = NadekoBot.BotConfig.CurrencyDropAmount;
|
||||
|
||||
if (dropAmount > 0)
|
||||
{
|
||||
firstPart = $"A random { NadekoBot.BotConfig.CurrencyName } appeared!";
|
||||
}
|
||||
else
|
||||
{
|
||||
firstPart = $"{dropAmount} random { NadekoBot.BotConfig.CurrencyPluralName } appeared!";
|
||||
}
|
||||
var file = GetRandomCurrencyImagePath();
|
||||
var sent = await channel.SendFileAsync(
|
||||
File.Open(file, FileMode.OpenOrCreate),
|
||||
new FileInfo(file).Name,
|
||||
$"❗ {firstPart} Pick it up by typing `{NadekoBot.ModulePrefixes[typeof(Games).Name]}pick`")
|
||||
.ConfigureAwait(false);
|
||||
var msgs = new IUserMessage[dropAmount];
|
||||
var prefix = NadekoBot.ModulePrefixes[typeof(Games).Name];
|
||||
var toSend = dropAmount == 1
|
||||
? GetLocalText(channel, "curgen_sn", NadekoBot.BotConfig.CurrencySign, prefix)
|
||||
: GetLocalText(channel, "curgen_pl", dropAmount, NadekoBot.BotConfig.CurrencySign, prefix);
|
||||
var file = GetRandomCurrencyImage();
|
||||
using (var fileStream = file.Value.ToStream())
|
||||
{
|
||||
var sent = await channel.SendFileAsync(
|
||||
fileStream,
|
||||
file.Key,
|
||||
toSend).ConfigureAwait(false);
|
||||
|
||||
msgs[0] = sent;
|
||||
msgs[0] = sent;
|
||||
}
|
||||
|
||||
plantedFlowers.AddOrUpdate(channel.Id, msgs.ToList(), (id, old) => { old.AddRange(msgs); return old; });
|
||||
plantedFlowers.AddOrUpdate(channel.Id, msgs.ToList(), (id, old) => { old.AddRange(msgs); return old; });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.GetCurrentClassLogger().Warn(ex);
|
||||
}
|
||||
});
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public static string GetLocalText(ITextChannel channel, string key, params object[] replacements) =>
|
||||
NadekoTopLevelModule.GetTextStatic(key,
|
||||
NadekoBot.Localization.GetCultureInfo(channel.GuildId),
|
||||
typeof(Games).Name.ToLowerInvariant(),
|
||||
replacements);
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task Pick()
|
||||
@ -117,32 +120,19 @@ namespace NadekoBot.Modules.Games
|
||||
|
||||
if (!(await channel.Guild.GetCurrentUserAsync()).GetPermissions(channel).ManageMessages)
|
||||
return;
|
||||
#if GLOBAL_NADEKO
|
||||
if (!usersRecentlyPicked.Add(Context.User.Id))
|
||||
|
||||
List<IUserMessage> msgs;
|
||||
|
||||
try { await Context.Message.DeleteAsync().ConfigureAwait(false); } catch { }
|
||||
if (!plantedFlowers.TryRemove(channel.Id, out msgs))
|
||||
return;
|
||||
#endif
|
||||
try
|
||||
{
|
||||
|
||||
List<IUserMessage> msgs;
|
||||
await Task.WhenAll(msgs.Where(m => m != null).Select(toDelete => toDelete.DeleteAsync())).ConfigureAwait(false);
|
||||
|
||||
try { await Context.Message.DeleteAsync().ConfigureAwait(false); } catch { }
|
||||
if (!plantedFlowers.TryRemove(channel.Id, out msgs))
|
||||
return;
|
||||
|
||||
await Task.WhenAll(msgs.Where(m => m != null).Select(toDelete => toDelete.DeleteAsync())).ConfigureAwait(false);
|
||||
|
||||
await CurrencyHandler.AddCurrencyAsync((IGuildUser)Context.User, $"Picked {NadekoBot.BotConfig.CurrencyPluralName}", msgs.Count, false).ConfigureAwait(false);
|
||||
var msg = await channel.SendConfirmAsync($"**{Context.User}** picked {msgs.Count}{NadekoBot.BotConfig.CurrencySign}!").ConfigureAwait(false);
|
||||
msg.DeleteAfter(10);
|
||||
}
|
||||
finally
|
||||
{
|
||||
#if GLOBAL_NADEKO
|
||||
await Task.Delay(60000);
|
||||
usersRecentlyPicked.TryRemove(Context.User.Id);
|
||||
#endif
|
||||
}
|
||||
await CurrencyHandler.AddCurrencyAsync((IGuildUser)Context.User, $"Picked {NadekoBot.BotConfig.CurrencyPluralName}", msgs.Count, false).ConfigureAwait(false);
|
||||
var msg = await ReplyConfirmLocalized("picked", msgs.Count + NadekoBot.BotConfig.CurrencySign)
|
||||
.ConfigureAwait(false);
|
||||
msg.DeleteAfter(10);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -155,22 +145,24 @@ namespace NadekoBot.Modules.Games
|
||||
var removed = await CurrencyHandler.RemoveCurrencyAsync((IGuildUser)Context.User, $"Planted a {NadekoBot.BotConfig.CurrencyName}", amount, false).ConfigureAwait(false);
|
||||
if (!removed)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync($"You don't have any {NadekoBot.BotConfig.CurrencyPluralName}.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("not_enough", NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
var file = GetRandomCurrencyImagePath();
|
||||
var imgData = GetRandomCurrencyImage();
|
||||
|
||||
//todo upload all currency images to transfer.sh and use that one as cdn
|
||||
//and then
|
||||
|
||||
var msgToSend = GetText("planted",
|
||||
Format.Bold(Context.User.ToString()),
|
||||
amount + NadekoBot.BotConfig.CurrencySign,
|
||||
Prefix);
|
||||
|
||||
IUserMessage msg;
|
||||
var vowelFirst = new[] { 'a', 'e', 'i', 'o', 'u' }.Contains(NadekoBot.BotConfig.CurrencyName[0]);
|
||||
|
||||
var msgToSend = $"Oh how Nice! **{Context.User.Username}** planted {(amount == 1 ? (vowelFirst ? "an" : "a") : amount.ToString())} {(amount > 1 ? NadekoBot.BotConfig.CurrencyPluralName : NadekoBot.BotConfig.CurrencyName)}. Pick it using {NadekoBot.ModulePrefixes[typeof(Games).Name]}pick";
|
||||
if (file == null)
|
||||
using (var toSend = imgData.Value.ToStream())
|
||||
{
|
||||
msg = await Context.Channel.SendConfirmAsync(NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
msg = await Context.Channel.SendFileAsync(File.Open(file, FileMode.OpenOrCreate), new FileInfo(file).Name, msgToSend).ConfigureAwait(false);
|
||||
msg = await Context.Channel.SendFileAsync(toSend, imgData.Key, msgToSend).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
var msgs = new IUserMessage[amount];
|
||||
@ -212,29 +204,20 @@ namespace NadekoBot.Modules.Games
|
||||
}
|
||||
if (enabled)
|
||||
{
|
||||
await channel.SendConfirmAsync("Currency generation enabled on this channel.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("curgen_enabled").ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await channel.SendConfirmAsync("Currency generation disabled on this channel.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("curgen_disabled").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetRandomCurrencyImagePath()
|
||||
private static KeyValuePair<string, ImmutableArray<byte>> GetRandomCurrencyImage()
|
||||
{
|
||||
var rng = new NadekoRandom();
|
||||
return Directory.GetFiles("data/currency_images").OrderBy(s => rng.Next()).FirstOrDefault();
|
||||
}
|
||||
var images = NadekoBot.Images.Currency;
|
||||
|
||||
int GetRandomNumber()
|
||||
{
|
||||
using (var rg = RandomNumberGenerator.Create())
|
||||
{
|
||||
byte[] rno = new byte[4];
|
||||
rg.GetBytes(rno);
|
||||
int randomvalue = BitConverter.ToInt32(rno, 0);
|
||||
return randomvalue;
|
||||
}
|
||||
return images[rng.Next(0, images.Length)];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,12 +3,10 @@ using Discord.Commands;
|
||||
using Discord.WebSocket;
|
||||
using NadekoBot.Attributes;
|
||||
using NadekoBot.Extensions;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NadekoBot.Modules.Games
|
||||
@ -16,7 +14,7 @@ namespace NadekoBot.Modules.Games
|
||||
public partial class Games
|
||||
{
|
||||
[Group]
|
||||
public class PollCommands : ModuleBase
|
||||
public class PollCommands : NadekoSubmodule
|
||||
{
|
||||
public static ConcurrentDictionary<ulong, Poll> ActivePolls = new ConcurrentDictionary<ulong, Poll>();
|
||||
|
||||
@ -24,20 +22,20 @@ namespace NadekoBot.Modules.Games
|
||||
[RequireUserPermission(GuildPermission.ManageMessages)]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public Task Poll([Remainder] string arg = null)
|
||||
=> InternalStartPoll(arg, isPublic: false);
|
||||
=> InternalStartPoll(arg, false);
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireUserPermission(GuildPermission.ManageMessages)]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public Task PublicPoll([Remainder] string arg = null)
|
||||
=> InternalStartPoll(arg, isPublic: true);
|
||||
=> InternalStartPoll(arg, true);
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireUserPermission(GuildPermission.ManageMessages)]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task PollStats()
|
||||
{
|
||||
Games.Poll poll;
|
||||
Poll poll;
|
||||
if (!ActivePolls.TryGetValue(Context.Guild.Id, out poll))
|
||||
return;
|
||||
|
||||
@ -48,8 +46,6 @@ namespace NadekoBot.Modules.Games
|
||||
{
|
||||
var channel = (ITextChannel)Context.Channel;
|
||||
|
||||
if (!(Context.User as IGuildUser).GuildPermissions.ManageChannels)
|
||||
return;
|
||||
if (string.IsNullOrWhiteSpace(arg) || !arg.Contains(";"))
|
||||
return;
|
||||
var data = arg.Split(';');
|
||||
@ -80,27 +76,25 @@ namespace NadekoBot.Modules.Games
|
||||
|
||||
public class Poll
|
||||
{
|
||||
private readonly IUserMessage originalMessage;
|
||||
private readonly IGuild guild;
|
||||
private string[] Answers { get; }
|
||||
private ConcurrentDictionary<ulong, int> participants = new ConcurrentDictionary<ulong, int>();
|
||||
private readonly string question;
|
||||
private DateTime started;
|
||||
private CancellationTokenSource pollCancellationSource = new CancellationTokenSource();
|
||||
private readonly IUserMessage _originalMessage;
|
||||
private readonly IGuild _guild;
|
||||
private string[] answers { get; }
|
||||
private readonly ConcurrentDictionary<ulong, int> _participants = new ConcurrentDictionary<ulong, int>();
|
||||
private readonly string _question;
|
||||
public bool IsPublic { get; }
|
||||
|
||||
public Poll(IUserMessage umsg, string question, IEnumerable<string> enumerable, bool isPublic = false)
|
||||
{
|
||||
this.originalMessage = umsg;
|
||||
this.guild = ((ITextChannel)umsg.Channel).Guild;
|
||||
this.question = question;
|
||||
this.Answers = enumerable as string[] ?? enumerable.ToArray();
|
||||
this.IsPublic = isPublic;
|
||||
_originalMessage = umsg;
|
||||
_guild = ((ITextChannel)umsg.Channel).Guild;
|
||||
_question = question;
|
||||
answers = enumerable as string[] ?? enumerable.ToArray();
|
||||
IsPublic = isPublic;
|
||||
}
|
||||
|
||||
public EmbedBuilder GetStats(string title)
|
||||
{
|
||||
var results = participants.GroupBy(kvp => kvp.Value)
|
||||
var results = _participants.GroupBy(kvp => kvp.Value)
|
||||
.ToDictionary(x => x.Key, x => x.Sum(kvp => 1))
|
||||
.OrderByDescending(kvp => kvp.Value)
|
||||
.ToArray();
|
||||
@ -108,7 +102,7 @@ namespace NadekoBot.Modules.Games
|
||||
var eb = new EmbedBuilder().WithTitle(title);
|
||||
|
||||
var sb = new StringBuilder()
|
||||
.AppendLine(Format.Bold(question))
|
||||
.AppendLine(Format.Bold(_question))
|
||||
.AppendLine();
|
||||
|
||||
var totalVotesCast = 0;
|
||||
@ -121,7 +115,7 @@ namespace NadekoBot.Modules.Games
|
||||
for (int i = 0; i < results.Length; i++)
|
||||
{
|
||||
var result = results[i];
|
||||
sb.AppendLine($"`{i + 1}.` {Format.Bold(Answers[result.Key - 1])} with {Format.Bold(result.Value.ToString())} votes.");
|
||||
sb.AppendLine($"`{i + 1}.` {Format.Bold(answers[result.Key - 1])} with {Format.Bold(result.Value.ToString())} votes.");
|
||||
totalVotesCast += result.Value;
|
||||
}
|
||||
}
|
||||
@ -135,22 +129,21 @@ namespace NadekoBot.Modules.Games
|
||||
|
||||
public async Task StartPoll()
|
||||
{
|
||||
started = DateTime.Now;
|
||||
NadekoBot.Client.MessageReceived += Vote;
|
||||
var msgToSend = $"📃**{originalMessage.Author.Username}** has created a poll which requires your attention:\n\n**{question}**\n";
|
||||
var msgToSend = $"📃**{_originalMessage.Author.Username}** has created a poll which requires your attention:\n\n**{_question}**\n";
|
||||
var num = 1;
|
||||
msgToSend = Answers.Aggregate(msgToSend, (current, answ) => current + $"`{num++}.` **{answ}**\n");
|
||||
msgToSend = answers.Aggregate(msgToSend, (current, answ) => current + $"`{num++}.` **{answ}**\n");
|
||||
if (!IsPublic)
|
||||
msgToSend += "\n**Private Message me with the corresponding number of the answer.**";
|
||||
else
|
||||
msgToSend += "\n**Send a Message here with the corresponding number of the answer.**";
|
||||
await originalMessage.Channel.SendConfirmAsync(msgToSend).ConfigureAwait(false);
|
||||
await _originalMessage.Channel.SendConfirmAsync(msgToSend).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task StopPoll()
|
||||
{
|
||||
NadekoBot.Client.MessageReceived -= Vote;
|
||||
await originalMessage.Channel.EmbedAsync(GetStats("POLL CLOSED")).ConfigureAwait(false);
|
||||
await _originalMessage.Channel.EmbedAsync(GetStats("POLL CLOSED")).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task Vote(SocketMessage imsg)
|
||||
@ -166,14 +159,14 @@ namespace NadekoBot.Modules.Games
|
||||
int vote;
|
||||
if (!int.TryParse(imsg.Content, out vote))
|
||||
return;
|
||||
if (vote < 1 || vote > Answers.Length)
|
||||
if (vote < 1 || vote > answers.Length)
|
||||
return;
|
||||
|
||||
IMessageChannel ch;
|
||||
if (IsPublic)
|
||||
{
|
||||
//if public, channel must be the same the poll started in
|
||||
if (originalMessage.Channel.Id != imsg.Channel.Id)
|
||||
if (_originalMessage.Channel.Id != imsg.Channel.Id)
|
||||
return;
|
||||
ch = imsg.Channel;
|
||||
}
|
||||
@ -184,13 +177,13 @@ namespace NadekoBot.Modules.Games
|
||||
return;
|
||||
|
||||
// user must be a member of the guild this poll is in
|
||||
var guildUsers = await guild.GetUsersAsync().ConfigureAwait(false);
|
||||
if (!guildUsers.Any(u => u.Id == imsg.Author.Id))
|
||||
var guildUsers = await _guild.GetUsersAsync().ConfigureAwait(false);
|
||||
if (guildUsers.All(u => u.Id != imsg.Author.Id))
|
||||
return;
|
||||
}
|
||||
|
||||
//user can vote only once
|
||||
if (participants.TryAdd(msg.Author.Id, vote))
|
||||
if (_participants.TryAdd(msg.Author.Id, vote))
|
||||
{
|
||||
if (!IsPublic)
|
||||
{
|
||||
|
@ -116,7 +116,7 @@ namespace NadekoBot.Modules.Games
|
||||
if (msg == null)
|
||||
return;
|
||||
|
||||
if (this.Channel == null || this.Channel.Id != this.Channel.Id) return;
|
||||
if (this.Channel == null || this.Channel.Id != msg.Channel.Id) return;
|
||||
|
||||
var guess = msg.Content;
|
||||
|
||||
@ -124,13 +124,14 @@ namespace NadekoBot.Modules.Games
|
||||
var decision = Judge(distance, guess.Length);
|
||||
if (decision && !finishedUserIds.Contains(msg.Author.Id))
|
||||
{
|
||||
var wpm = CurrentSentence.Length / WORD_VALUE / sw.Elapsed.Seconds * 60;
|
||||
var elapsed = sw.Elapsed;
|
||||
var wpm = CurrentSentence.Length / WORD_VALUE / elapsed.TotalSeconds * 60;
|
||||
finishedUserIds.Add(msg.Author.Id);
|
||||
await this.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||
.WithTitle((string)$"{msg.Author} finished the race!")
|
||||
.WithTitle($"{msg.Author} finished the race!")
|
||||
.AddField(efb => efb.WithName("Place").WithValue($"#{finishedUserIds.Count}").WithIsInline(true))
|
||||
.AddField(efb => efb.WithName("WPM").WithValue($"{wpm:F2} *[{sw.Elapsed.Seconds.ToString()}sec]*").WithIsInline(true))
|
||||
.AddField(efb => efb.WithName((string)"Errors").WithValue((string)distance.ToString()).WithIsInline((bool)true)))
|
||||
.AddField(efb => efb.WithName("WPM").WithValue($"{wpm:F1} *[{elapsed.TotalSeconds:F2}sec]*").WithIsInline(true))
|
||||
.AddField(efb => efb.WithName("Errors").WithValue(distance.ToString()).WithIsInline(true)))
|
||||
.ConfigureAwait(false);
|
||||
if (finishedUserIds.Count % 4 == 0)
|
||||
{
|
||||
@ -146,15 +147,15 @@ namespace NadekoBot.Modules.Games
|
||||
}
|
||||
|
||||
[Group]
|
||||
public class SpeedTypingCommands : ModuleBase
|
||||
public class SpeedTypingCommands : NadekoSubmodule
|
||||
{
|
||||
public static List<TypingArticle> TypingArticles { get; } = new List<TypingArticle>();
|
||||
|
||||
const string typingArticlesPath = "data/typing_articles.json";
|
||||
private const string _typingArticlesPath = "data/typing_articles.json";
|
||||
|
||||
static SpeedTypingCommands()
|
||||
{
|
||||
try { TypingArticles = JsonConvert.DeserializeObject<List<TypingArticle>>(File.ReadAllText(typingArticlesPath)); } catch { }
|
||||
try { TypingArticles = JsonConvert.DeserializeObject<List<TypingArticle>>(File.ReadAllText(_typingArticlesPath)); } catch { }
|
||||
}
|
||||
public static ConcurrentDictionary<ulong, TypingGame> RunningContests = new ConcurrentDictionary<ulong, TypingGame>();
|
||||
|
||||
@ -207,7 +208,7 @@ namespace NadekoBot.Modules.Games
|
||||
Text = text.SanitizeMentions(),
|
||||
});
|
||||
|
||||
File.WriteAllText(typingArticlesPath, JsonConvert.SerializeObject(TypingArticles));
|
||||
File.WriteAllText(_typingArticlesPath, JsonConvert.SerializeObject(TypingArticles));
|
||||
|
||||
await channel.SendConfirmAsync("Added new article for typing game.").ConfigureAwait(false);
|
||||
}
|
||||
@ -221,7 +222,7 @@ namespace NadekoBot.Modules.Games
|
||||
if (page < 1)
|
||||
return;
|
||||
|
||||
var articles = TypingArticles.Skip((page - 1) * 15).Take(15);
|
||||
var articles = TypingArticles.Skip((page - 1) * 15).Take(15).ToArray();
|
||||
|
||||
if (!articles.Any())
|
||||
{
|
||||
@ -229,7 +230,7 @@ namespace NadekoBot.Modules.Games
|
||||
return;
|
||||
}
|
||||
var i = (page - 1) * 15;
|
||||
await channel.SendConfirmAsync("List of articles for Type Race", String.Join("\n", articles.Select(a => $"`#{++i}` - {a.Text.TrimTo(50)}")))
|
||||
await channel.SendConfirmAsync("List of articles for Type Race", string.Join("\n", articles.Select(a => $"`#{++i}` - {a.Text.TrimTo(50)}")))
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@ -247,7 +248,7 @@ namespace NadekoBot.Modules.Games
|
||||
var removed = TypingArticles[index];
|
||||
TypingArticles.RemoveAt(index);
|
||||
|
||||
File.WriteAllText(typingArticlesPath, JsonConvert.SerializeObject(TypingArticles));
|
||||
File.WriteAllText(_typingArticlesPath, JsonConvert.SerializeObject(TypingArticles));
|
||||
|
||||
await channel.SendConfirmAsync($"`Removed typing article:` #{index + 1} - {removed.Text.TrimTo(50)}")
|
||||
.ConfigureAwait(false);
|
||||
|
320
src/NadekoBot/Modules/Games/Commands/TicTacToe.cs
Normal file
320
src/NadekoBot/Modules/Games/Commands/TicTacToe.cs
Normal file
@ -0,0 +1,320 @@
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using NadekoBot.Attributes;
|
||||
using NadekoBot.Extensions;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NadekoBot.Modules.Games
|
||||
{
|
||||
public partial class Games
|
||||
{
|
||||
[Group]
|
||||
public class TicTacToeCommands : NadekoSubmodule
|
||||
{
|
||||
//channelId/game
|
||||
private static readonly Dictionary<ulong, TicTacToe> _games = new Dictionary<ulong, TicTacToe>();
|
||||
|
||||
private readonly SemaphoreSlim _sem = new SemaphoreSlim(1, 1);
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task TicTacToe()
|
||||
{
|
||||
var channel = (ITextChannel)Context.Channel;
|
||||
|
||||
await _sem.WaitAsync(1000);
|
||||
try
|
||||
{
|
||||
TicTacToe game;
|
||||
if (_games.TryGetValue(channel.Id, out game))
|
||||
{
|
||||
var _ = Task.Run(async () =>
|
||||
{
|
||||
await game.Start((IGuildUser)Context.User);
|
||||
});
|
||||
return;
|
||||
}
|
||||
game = new TicTacToe(channel, (IGuildUser)Context.User);
|
||||
_games.Add(channel.Id, game);
|
||||
await ReplyConfirmLocalized("ttt_created").ConfigureAwait(false);
|
||||
|
||||
game.OnEnded += (g) =>
|
||||
{
|
||||
_games.Remove(channel.Id);
|
||||
};
|
||||
}
|
||||
finally
|
||||
{
|
||||
_sem.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class TicTacToe
|
||||
{
|
||||
enum Phase
|
||||
{
|
||||
Starting,
|
||||
Started,
|
||||
Ended
|
||||
}
|
||||
|
||||
private readonly ITextChannel _channel;
|
||||
private readonly IGuildUser[] _users;
|
||||
private readonly int?[,] _state;
|
||||
private Phase _phase;
|
||||
private int _curUserIndex;
|
||||
private readonly SemaphoreSlim _moveLock;
|
||||
|
||||
private IGuildUser _winner;
|
||||
|
||||
private readonly string[] _numbers = { ":one:", ":two:", ":three:", ":four:", ":five:", ":six:", ":seven:", ":eight:", ":nine:" };
|
||||
|
||||
public Action<TicTacToe> OnEnded;
|
||||
|
||||
private IUserMessage _previousMessage;
|
||||
private Timer _timeoutTimer;
|
||||
|
||||
public TicTacToe(ITextChannel channel, IGuildUser firstUser)
|
||||
{
|
||||
_channel = channel;
|
||||
_users = new[] { firstUser, null };
|
||||
_state = new int?[,] {
|
||||
{ null, null, null },
|
||||
{ null, null, null },
|
||||
{ null, null, null },
|
||||
};
|
||||
|
||||
_phase = Phase.Starting;
|
||||
_moveLock = new SemaphoreSlim(1, 1);
|
||||
}
|
||||
|
||||
private string GetText(string key, params object[] replacements) =>
|
||||
NadekoTopLevelModule.GetTextStatic(key,
|
||||
NadekoBot.Localization.GetCultureInfo(_channel.GuildId),
|
||||
typeof(Games).Name.ToLowerInvariant(),
|
||||
replacements);
|
||||
|
||||
public string GetState()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
for (var i = 0; i < _state.GetLength(0); i++)
|
||||
{
|
||||
for (var j = 0; j < _state.GetLength(1); j++)
|
||||
{
|
||||
sb.Append(_state[i, j] == null ? _numbers[i * 3 + j] : GetIcon(_state[i, j]));
|
||||
if (j < _state.GetLength(1) - 1)
|
||||
sb.Append("┃");
|
||||
}
|
||||
if (i < _state.GetLength(0) - 1)
|
||||
sb.AppendLine("\n──────────");
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public EmbedBuilder GetEmbed(string title = null)
|
||||
{
|
||||
var embed = new EmbedBuilder()
|
||||
.WithOkColor()
|
||||
.WithDescription(Environment.NewLine + GetState())
|
||||
.WithAuthor(eab => eab.WithName(GetText("vs", _users[0], _users[1])));
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(title))
|
||||
embed.WithTitle(title);
|
||||
|
||||
if (_winner == null)
|
||||
{
|
||||
if (_phase == Phase.Ended)
|
||||
embed.WithFooter(efb => efb.WithText(GetText("ttt_no_moves")));
|
||||
else
|
||||
embed.WithFooter(efb => efb.WithText(GetText("ttt_users_move", _users[_curUserIndex])));
|
||||
}
|
||||
else
|
||||
embed.WithFooter(efb => efb.WithText(GetText("ttt_has_won", _winner)));
|
||||
|
||||
return embed;
|
||||
}
|
||||
|
||||
private static string GetIcon(int? val)
|
||||
{
|
||||
switch (val)
|
||||
{
|
||||
case 0:
|
||||
return "❌";
|
||||
case 1:
|
||||
return "⭕";
|
||||
case 2:
|
||||
return "❎";
|
||||
case 3:
|
||||
return "🅾";
|
||||
default:
|
||||
return "⬛";
|
||||
}
|
||||
}
|
||||
|
||||
public async Task Start(IGuildUser user)
|
||||
{
|
||||
if (_phase == Phase.Started || _phase == Phase.Ended)
|
||||
{
|
||||
await _channel.SendErrorAsync(user.Mention + GetText("ttt_already_running")).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
else if (_users[0] == user)
|
||||
{
|
||||
await _channel.SendErrorAsync(user.Mention + GetText("ttt_against_yourself")).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
_users[1] = user;
|
||||
|
||||
_phase = Phase.Started;
|
||||
|
||||
_timeoutTimer = new Timer(async (_) =>
|
||||
{
|
||||
await _moveLock.WaitAsync();
|
||||
try
|
||||
{
|
||||
if (_phase == Phase.Ended)
|
||||
return;
|
||||
|
||||
_phase = Phase.Ended;
|
||||
if (_users[1] != null)
|
||||
{
|
||||
_winner = _users[_curUserIndex ^= 1];
|
||||
var del = _previousMessage?.DeleteAsync();
|
||||
try
|
||||
{
|
||||
await _channel.EmbedAsync(GetEmbed(GetText("ttt_time_expired"))).ConfigureAwait(false);
|
||||
if (del != null)
|
||||
await del.ConfigureAwait(false);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
OnEnded?.Invoke(this);
|
||||
}
|
||||
catch { }
|
||||
finally
|
||||
{
|
||||
_moveLock.Release();
|
||||
}
|
||||
}, null, 15000, Timeout.Infinite);
|
||||
|
||||
NadekoBot.Client.MessageReceived += Client_MessageReceived;
|
||||
|
||||
|
||||
_previousMessage = await _channel.EmbedAsync(GetEmbed(GetText("game_started"))).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private bool IsDraw()
|
||||
{
|
||||
for (var i = 0; i < 3; i++)
|
||||
{
|
||||
for (var j = 0; j < 3; j++)
|
||||
{
|
||||
if (_state[i, j] == null)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private Task Client_MessageReceived(Discord.WebSocket.SocketMessage msg)
|
||||
{
|
||||
var _ = Task.Run(async () =>
|
||||
{
|
||||
await _moveLock.WaitAsync().ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
var curUser = _users[_curUserIndex];
|
||||
if (_phase == Phase.Ended || msg.Author?.Id != curUser.Id)
|
||||
return;
|
||||
|
||||
int index;
|
||||
if (int.TryParse(msg.Content, out index) &&
|
||||
--index >= 0 &&
|
||||
index <= 9 &&
|
||||
_state[index / 3, index % 3] == null)
|
||||
{
|
||||
_state[index / 3, index % 3] = _curUserIndex;
|
||||
|
||||
// i'm lazy
|
||||
if (_state[index / 3, 0] == _state[index / 3, 1] && _state[index / 3, 1] == _state[index / 3, 2])
|
||||
{
|
||||
_state[index / 3, 0] = _curUserIndex + 2;
|
||||
_state[index / 3, 1] = _curUserIndex + 2;
|
||||
_state[index / 3, 2] = _curUserIndex + 2;
|
||||
|
||||
_phase = Phase.Ended;
|
||||
}
|
||||
else if (_state[0, index % 3] == _state[1, index % 3] && _state[1, index % 3] == _state[2, index % 3])
|
||||
{
|
||||
_state[0, index % 3] = _curUserIndex + 2;
|
||||
_state[1, index % 3] = _curUserIndex + 2;
|
||||
_state[2, index % 3] = _curUserIndex + 2;
|
||||
|
||||
_phase = Phase.Ended;
|
||||
}
|
||||
else if (_curUserIndex == _state[0, 0] && _state[0, 0] == _state[1, 1] && _state[1, 1] == _state[2, 2])
|
||||
{
|
||||
_state[0, 0] = _curUserIndex + 2;
|
||||
_state[1, 1] = _curUserIndex + 2;
|
||||
_state[2, 2] = _curUserIndex + 2;
|
||||
|
||||
_phase = Phase.Ended;
|
||||
}
|
||||
else if (_curUserIndex == _state[0, 2] && _state[0, 2] == _state[1, 1] && _state[1, 1] == _state[2, 0])
|
||||
{
|
||||
_state[0, 2] = _curUserIndex + 2;
|
||||
_state[1, 1] = _curUserIndex + 2;
|
||||
_state[2, 0] = _curUserIndex + 2;
|
||||
|
||||
_phase = Phase.Ended;
|
||||
}
|
||||
var reason = "";
|
||||
|
||||
if (_phase == Phase.Ended) // if user won, stop receiving moves
|
||||
{
|
||||
reason = GetText("ttt_matched_three");
|
||||
_winner = _users[_curUserIndex];
|
||||
NadekoBot.Client.MessageReceived -= Client_MessageReceived;
|
||||
OnEnded?.Invoke(this);
|
||||
}
|
||||
else if (IsDraw())
|
||||
{
|
||||
reason = GetText("ttt_a_draw");
|
||||
_phase = Phase.Ended;
|
||||
NadekoBot.Client.MessageReceived -= Client_MessageReceived;
|
||||
OnEnded?.Invoke(this);
|
||||
}
|
||||
|
||||
var sendstate = Task.Run(async () =>
|
||||
{
|
||||
var del1 = msg.DeleteAsync();
|
||||
var del2 = _previousMessage?.DeleteAsync();
|
||||
try { _previousMessage = await _channel.EmbedAsync(GetEmbed(reason)); } catch { }
|
||||
try { await del1; } catch { }
|
||||
try { if (del2 != null) await del2; } catch { }
|
||||
});
|
||||
_curUserIndex ^= 1;
|
||||
|
||||
_timeoutTimer.Change(15000, Timeout.Infinite);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_moveLock.Release();
|
||||
}
|
||||
});
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -17,36 +17,42 @@ namespace NadekoBot.Modules.Games.Trivia
|
||||
public class TriviaGame
|
||||
{
|
||||
private readonly SemaphoreSlim _guessLock = new SemaphoreSlim(1, 1);
|
||||
private Logger _log { get; }
|
||||
private readonly Logger _log;
|
||||
|
||||
public IGuild guild { get; }
|
||||
public ITextChannel channel { get; }
|
||||
public IGuild Guild { get; }
|
||||
public ITextChannel Channel { get; }
|
||||
|
||||
private int QuestionDurationMiliseconds { get; } = 30000;
|
||||
private int HintTimeoutMiliseconds { get; } = 6000;
|
||||
public bool ShowHints { get; } = true;
|
||||
private int questionDurationMiliseconds { get; } = 30000;
|
||||
private int hintTimeoutMiliseconds { get; } = 6000;
|
||||
public bool ShowHints { get; }
|
||||
private CancellationTokenSource triviaCancelSource { get; set; }
|
||||
|
||||
public TriviaQuestion CurrentQuestion { get; private set; }
|
||||
public HashSet<TriviaQuestion> oldQuestions { get; } = new HashSet<TriviaQuestion>();
|
||||
public HashSet<TriviaQuestion> OldQuestions { get; } = new HashSet<TriviaQuestion>();
|
||||
|
||||
public ConcurrentDictionary<IGuildUser, int> Users { get; } = new ConcurrentDictionary<IGuildUser, int>();
|
||||
|
||||
public bool GameActive { get; private set; } = false;
|
||||
public bool GameActive { get; private set; }
|
||||
public bool ShouldStopGame { get; private set; }
|
||||
|
||||
public int WinRequirement { get; } = 10;
|
||||
public int WinRequirement { get; }
|
||||
|
||||
public TriviaGame(IGuild guild, ITextChannel channel, bool showHints, int winReq)
|
||||
{
|
||||
this._log = LogManager.GetCurrentClassLogger();
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
this.ShowHints = showHints;
|
||||
this.guild = guild;
|
||||
this.channel = channel;
|
||||
this.WinRequirement = winReq;
|
||||
ShowHints = showHints;
|
||||
Guild = guild;
|
||||
Channel = channel;
|
||||
WinRequirement = winReq;
|
||||
}
|
||||
|
||||
private string GetText(string key, params object[] replacements) =>
|
||||
NadekoTopLevelModule.GetTextStatic(key,
|
||||
NadekoBot.Localization.GetCultureInfo(Channel.GuildId),
|
||||
typeof(Games).Name.ToLowerInvariant(),
|
||||
replacements);
|
||||
|
||||
public async Task StartGame()
|
||||
{
|
||||
while (!ShouldStopGame)
|
||||
@ -55,26 +61,24 @@ namespace NadekoBot.Modules.Games.Trivia
|
||||
triviaCancelSource = new CancellationTokenSource();
|
||||
|
||||
// load question
|
||||
CurrentQuestion = TriviaQuestionPool.Instance.GetRandomQuestion(oldQuestions);
|
||||
if (CurrentQuestion == null ||
|
||||
string.IsNullOrWhiteSpace(CurrentQuestion.Answer) ||
|
||||
string.IsNullOrWhiteSpace(CurrentQuestion.Question))
|
||||
CurrentQuestion = TriviaQuestionPool.Instance.GetRandomQuestion(OldQuestions);
|
||||
if (string.IsNullOrWhiteSpace(CurrentQuestion?.Answer) || string.IsNullOrWhiteSpace(CurrentQuestion.Question))
|
||||
{
|
||||
await channel.SendErrorAsync("Trivia Game", "Failed loading a question.").ConfigureAwait(false);
|
||||
await Channel.SendErrorAsync(GetText("trivia_game"), GetText("failed_loading_question")).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
oldQuestions.Add(CurrentQuestion); //add it to exclusion list so it doesn't show up again
|
||||
OldQuestions.Add(CurrentQuestion); //add it to exclusion list so it doesn't show up again
|
||||
|
||||
EmbedBuilder questionEmbed;
|
||||
IUserMessage questionMessage;
|
||||
try
|
||||
{
|
||||
questionEmbed = new EmbedBuilder().WithOkColor()
|
||||
.WithTitle("Trivia Game")
|
||||
.AddField(eab => eab.WithName("Category").WithValue(CurrentQuestion.Category))
|
||||
.AddField(eab => eab.WithName("Question").WithValue(CurrentQuestion.Question));
|
||||
.WithTitle(GetText("trivia_game"))
|
||||
.AddField(eab => eab.WithName(GetText("category")).WithValue(CurrentQuestion.Category))
|
||||
.AddField(eab => eab.WithName(GetText("question")).WithValue(CurrentQuestion.Question));
|
||||
|
||||
questionMessage = await channel.EmbedAsync(questionEmbed).ConfigureAwait(false);
|
||||
questionMessage = await Channel.EmbedAsync(questionEmbed).ConfigureAwait(false);
|
||||
}
|
||||
catch (HttpException ex) when (ex.HttpCode == System.Net.HttpStatusCode.NotFound ||
|
||||
ex.HttpCode == System.Net.HttpStatusCode.Forbidden ||
|
||||
@ -99,7 +103,7 @@ namespace NadekoBot.Modules.Games.Trivia
|
||||
try
|
||||
{
|
||||
//hint
|
||||
await Task.Delay(HintTimeoutMiliseconds, triviaCancelSource.Token).ConfigureAwait(false);
|
||||
await Task.Delay(hintTimeoutMiliseconds, triviaCancelSource.Token).ConfigureAwait(false);
|
||||
if (ShowHints)
|
||||
try
|
||||
{
|
||||
@ -113,7 +117,7 @@ namespace NadekoBot.Modules.Games.Trivia
|
||||
catch (Exception ex) { _log.Warn(ex); }
|
||||
|
||||
//timeout
|
||||
await Task.Delay(QuestionDurationMiliseconds - HintTimeoutMiliseconds, triviaCancelSource.Token).ConfigureAwait(false);
|
||||
await Task.Delay(questionDurationMiliseconds - hintTimeoutMiliseconds, triviaCancelSource.Token).ConfigureAwait(false);
|
||||
|
||||
}
|
||||
catch (TaskCanceledException) { } //means someone guessed the answer
|
||||
@ -124,7 +128,7 @@ namespace NadekoBot.Modules.Games.Trivia
|
||||
NadekoBot.Client.MessageReceived -= PotentialGuess;
|
||||
}
|
||||
if (!triviaCancelSource.IsCancellationRequested)
|
||||
try { await channel.SendErrorAsync("Trivia Game", $"**Time's up!** The correct answer was **{CurrentQuestion.Answer}**").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
|
||||
try { await Channel.SendErrorAsync(GetText("trivia_game"), GetText("trivia_times_up", Format.Bold(CurrentQuestion.Answer))).ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
|
||||
await Task.Delay(2000).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@ -133,7 +137,7 @@ namespace NadekoBot.Modules.Games.Trivia
|
||||
{
|
||||
ShouldStopGame = true;
|
||||
|
||||
await channel.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||
await Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||
.WithAuthor(eab => eab.WithName("Trivia Game Ended"))
|
||||
.WithTitle("Final Results")
|
||||
.WithDescription(GetLeaderboard())).ConfigureAwait(false);
|
||||
@ -144,7 +148,7 @@ namespace NadekoBot.Modules.Games.Trivia
|
||||
var old = ShouldStopGame;
|
||||
ShouldStopGame = true;
|
||||
if (!old)
|
||||
try { await channel.SendConfirmAsync("Trivia Game", "Stopping after this question.").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
|
||||
try { await Channel.SendConfirmAsync(GetText("trivia_game"), GetText("trivia_stopping")).ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
|
||||
}
|
||||
|
||||
private async Task PotentialGuess(SocketMessage imsg)
|
||||
@ -155,11 +159,9 @@ namespace NadekoBot.Modules.Games.Trivia
|
||||
return;
|
||||
|
||||
var umsg = imsg as SocketUserMessage;
|
||||
if (umsg == null)
|
||||
return;
|
||||
|
||||
var textChannel = umsg.Channel as ITextChannel;
|
||||
if (textChannel == null || textChannel.Guild != guild)
|
||||
var textChannel = umsg?.Channel as ITextChannel;
|
||||
if (textChannel == null || textChannel.Guild != Guild)
|
||||
return;
|
||||
|
||||
var guildUser = (IGuildUser)umsg.Author;
|
||||
@ -182,13 +184,24 @@ namespace NadekoBot.Modules.Games.Trivia
|
||||
if (Users[guildUser] == WinRequirement)
|
||||
{
|
||||
ShouldStopGame = true;
|
||||
try { await channel.SendConfirmAsync("Trivia Game", $"{guildUser.Mention} guessed it and WON the game! The answer was: **{CurrentQuestion.Answer}**").ConfigureAwait(false); } catch { }
|
||||
try
|
||||
{
|
||||
await Channel.SendConfirmAsync(GetText("trivia_game"),
|
||||
GetText("trivia_win",
|
||||
guildUser.Mention,
|
||||
Format.Bold(CurrentQuestion.Answer))).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
var reward = NadekoBot.BotConfig.TriviaCurrencyReward;
|
||||
if (reward > 0)
|
||||
await CurrencyHandler.AddCurrencyAsync(guildUser, "Won trivia", reward, true).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
await channel.SendConfirmAsync("Trivia Game", $"{guildUser.Mention} guessed it! The answer was: **{CurrentQuestion.Answer}**").ConfigureAwait(false);
|
||||
await Channel.SendConfirmAsync(GetText("trivia_game"),
|
||||
GetText("trivia_guess", guildUser.Mention, Format.Bold(CurrentQuestion.Answer))).ConfigureAwait(false);
|
||||
|
||||
}
|
||||
catch (Exception ex) { _log.Warn(ex); }
|
||||
@ -197,13 +210,13 @@ namespace NadekoBot.Modules.Games.Trivia
|
||||
public string GetLeaderboard()
|
||||
{
|
||||
if (Users.Count == 0)
|
||||
return "No results.";
|
||||
return GetText("no_results");
|
||||
|
||||
var sb = new StringBuilder();
|
||||
|
||||
foreach (var kvp in Users.OrderByDescending(kvp => kvp.Value))
|
||||
{
|
||||
sb.AppendLine($"**{kvp.Key.Username}** has {kvp.Value} points".ToString().SnPl(kvp.Value));
|
||||
sb.AppendLine(GetText("trivia_points", Format.Bold(kvp.Key.ToString()), kvp.Value).SnPl(kvp.Value));
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
|
@ -98,7 +98,7 @@ namespace NadekoBot.Modules.Games.Trivia
|
||||
if (letters[i] != ' ')
|
||||
letters[i] = '_';
|
||||
}
|
||||
return string.Join(" \x200B", new string(letters).Replace(" ", " \x200B").AsEnumerable());
|
||||
return string.Join(" ", new string(letters).Replace(" ", " \u2000").AsEnumerable());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,9 +3,7 @@ using Discord.Commands;
|
||||
using NadekoBot.Attributes;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Modules.Games.Trivia;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
||||
@ -14,7 +12,7 @@ namespace NadekoBot.Modules.Games
|
||||
public partial class Games
|
||||
{
|
||||
[Group]
|
||||
public class TriviaCommands : ModuleBase
|
||||
public class TriviaCommands : NadekoSubmodule
|
||||
{
|
||||
public static ConcurrentDictionary<ulong, TriviaGame> RunningTrivias { get; } = new ConcurrentDictionary<ulong, TriviaGame>();
|
||||
|
||||
@ -31,7 +29,7 @@ namespace NadekoBot.Modules.Games
|
||||
|
||||
var showHints = !additionalArgs.Contains("nohint");
|
||||
|
||||
TriviaGame trivia = new TriviaGame(channel.Guild, channel, showHints, winReq);
|
||||
var trivia = new TriviaGame(channel.Guild, channel, showHints, winReq);
|
||||
if (RunningTrivias.TryAdd(channel.Guild.Id, trivia))
|
||||
{
|
||||
try
|
||||
@ -45,8 +43,9 @@ namespace NadekoBot.Modules.Games
|
||||
}
|
||||
return;
|
||||
}
|
||||
else
|
||||
await Context.Channel.SendErrorAsync("Trivia game is already running on this server.\n" + trivia.CurrentQuestion).ConfigureAwait(false);
|
||||
|
||||
await Context.Channel.SendErrorAsync(GetText("trivia_already_running") + "\n" + trivia.CurrentQuestion)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -58,11 +57,11 @@ namespace NadekoBot.Modules.Games
|
||||
TriviaGame trivia;
|
||||
if (RunningTrivias.TryGetValue(channel.Guild.Id, out trivia))
|
||||
{
|
||||
await channel.SendConfirmAsync("Leaderboard", trivia.GetLeaderboard()).ConfigureAwait(false);
|
||||
await channel.SendConfirmAsync(GetText("leaderboard"), trivia.GetLeaderboard()).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
await channel.SendErrorAsync("No trivia is running on this server.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("trivia_none").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -78,7 +77,7 @@ namespace NadekoBot.Modules.Games
|
||||
return;
|
||||
}
|
||||
|
||||
await channel.SendErrorAsync("No trivia is running on this server.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("trivia_none").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,17 +4,29 @@ using NadekoBot.Services;
|
||||
using System.Threading.Tasks;
|
||||
using NadekoBot.Attributes;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using NadekoBot.Extensions;
|
||||
using System.Net.Http;
|
||||
using ImageSharp;
|
||||
using NadekoBot.DataStructures;
|
||||
using NLog;
|
||||
|
||||
namespace NadekoBot.Modules.Games
|
||||
{
|
||||
[NadekoModule("Games", ">")]
|
||||
public partial class Games : DiscordModule
|
||||
public partial class Games : NadekoTopLevelModule
|
||||
{
|
||||
private static string[] _8BallResponses { get; } = NadekoBot.BotConfig.EightBallResponses.Select(ebr => ebr.Text).ToArray();
|
||||
private static readonly ImmutableArray<string> _8BallResponses = NadekoBot.BotConfig.EightBallResponses.Select(ebr => ebr.Text).ToImmutableArray();
|
||||
|
||||
private static readonly Timer _t = new Timer((_) =>
|
||||
{
|
||||
_girlRatings.Clear();
|
||||
|
||||
}, null, TimeSpan.FromDays(1), TimeSpan.FromDays(1));
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task Choose([Remainder] string list = null)
|
||||
@ -22,7 +34,7 @@ namespace NadekoBot.Modules.Games
|
||||
if (string.IsNullOrWhiteSpace(list))
|
||||
return;
|
||||
var listArr = list.Split(';');
|
||||
if (listArr.Count() < 2)
|
||||
if (listArr.Length < 2)
|
||||
return;
|
||||
var rng = new NadekoRandom();
|
||||
await Context.Channel.SendConfirmAsync("🤔", listArr[rng.Next(0, listArr.Length)]).ConfigureAwait(false);
|
||||
@ -33,24 +45,26 @@ namespace NadekoBot.Modules.Games
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(question))
|
||||
return;
|
||||
var rng = new NadekoRandom();
|
||||
|
||||
await Context.Channel.EmbedAsync(new EmbedBuilder().WithColor(NadekoBot.OkColor)
|
||||
.AddField(efb => efb.WithName("❓ Question").WithValue(question).WithIsInline(false))
|
||||
.AddField(efb => efb.WithName("🎱 8Ball").WithValue(_8BallResponses[new NadekoRandom().Next(0, _8BallResponses.Length)]).WithIsInline(false)));
|
||||
.AddField(efb => efb.WithName("❓ " + GetText("question") ).WithValue(question).WithIsInline(false))
|
||||
.AddField(efb => efb.WithName("🎱 " + GetText("8ball")).WithValue(_8BallResponses[new NadekoRandom().Next(0, _8BallResponses.Length)]).WithIsInline(false)));
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task Rps(string input)
|
||||
{
|
||||
Func<int,string> GetRPSPick = (p) =>
|
||||
Func<int,string> getRpsPick = (p) =>
|
||||
{
|
||||
if (p == 0)
|
||||
return "🚀";
|
||||
else if (p == 1)
|
||||
return "📎";
|
||||
else
|
||||
return "✂️";
|
||||
switch (p)
|
||||
{
|
||||
case 0:
|
||||
return "🚀";
|
||||
case 1:
|
||||
return "📎";
|
||||
default:
|
||||
return "✂️";
|
||||
}
|
||||
};
|
||||
|
||||
int pick;
|
||||
@ -74,19 +88,176 @@ namespace NadekoBot.Modules.Games
|
||||
return;
|
||||
}
|
||||
var nadekoPick = new NadekoRandom().Next(0, 3);
|
||||
var msg = "";
|
||||
string msg;
|
||||
if (pick == nadekoPick)
|
||||
msg = $"It's a draw! Both picked {GetRPSPick(pick)}";
|
||||
msg = GetText("rps_draw", getRpsPick(pick));
|
||||
else if ((pick == 0 && nadekoPick == 1) ||
|
||||
(pick == 1 && nadekoPick == 2) ||
|
||||
(pick == 2 && nadekoPick == 0))
|
||||
msg = $"{NadekoBot.Client.CurrentUser.Mention} won! {GetRPSPick(nadekoPick)} beats {GetRPSPick(pick)}";
|
||||
msg = GetText("rps_win", NadekoBot.Client.CurrentUser.Mention,
|
||||
getRpsPick(nadekoPick), getRpsPick(pick));
|
||||
else
|
||||
msg = $"{Context.User.Mention} won! {GetRPSPick(pick)} beats {GetRPSPick(nadekoPick)}";
|
||||
msg = GetText("rps_win", Context.User.Mention, getRpsPick(pick),
|
||||
getRpsPick(nadekoPick));
|
||||
|
||||
await Context.Channel.SendConfirmAsync(msg).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private static readonly ConcurrentDictionary<ulong, GirlRating> _girlRatings = new ConcurrentDictionary<ulong, GirlRating>();
|
||||
|
||||
public class GirlRating
|
||||
{
|
||||
private static readonly Logger _log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
public double Crazy { get; }
|
||||
public double Hot { get; }
|
||||
public int Roll { get; }
|
||||
public string Advice { get; }
|
||||
public AsyncLazy<string> Url { get; }
|
||||
|
||||
public GirlRating(double crazy, double hot, int roll, string advice)
|
||||
{
|
||||
Crazy = crazy;
|
||||
Hot = hot;
|
||||
Roll = roll;
|
||||
Advice = advice; // convenient to have it here, even though atm there are only few different ones.
|
||||
|
||||
Url = new AsyncLazy<string>(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var ms = new MemoryStream(NadekoBot.Images.WifeMatrix.ToArray(), false))
|
||||
using (var img = new ImageSharp.Image(ms))
|
||||
{
|
||||
const int minx = 35;
|
||||
const int miny = 385;
|
||||
const int length = 345;
|
||||
|
||||
var pointx = (int)(minx + length * (Hot / 10));
|
||||
var pointy = (int)(miny - length * ((Crazy - 4) / 6));
|
||||
|
||||
using (var pointMs = new MemoryStream(NadekoBot.Images.RategirlDot.ToArray(), false))
|
||||
using (var pointImg = new ImageSharp.Image(pointMs))
|
||||
{
|
||||
img.DrawImage(pointImg, 100, default(Size), new Point(pointx - 10, pointy - 10));
|
||||
}
|
||||
|
||||
string url;
|
||||
using (var http = new HttpClient())
|
||||
using (var imgStream = new MemoryStream())
|
||||
{
|
||||
img.Save(imgStream);
|
||||
var byteContent = new ByteArrayContent(imgStream.ToArray());
|
||||
http.AddFakeHeaders();
|
||||
|
||||
var reponse = await http.PutAsync("https://transfer.sh/img.png", byteContent);
|
||||
url = await reponse.Content.ReadAsStringAsync();
|
||||
}
|
||||
return url;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.Warn(ex);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task RateGirl(IGuildUser usr)
|
||||
{
|
||||
var gr = _girlRatings.GetOrAdd(usr.Id, GetGirl);
|
||||
var img = await gr.Url;
|
||||
await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||
.WithTitle("Girl Rating For " + usr)
|
||||
.AddField(efb => efb.WithName("Hot").WithValue(gr.Hot.ToString("F2")).WithIsInline(true))
|
||||
.AddField(efb => efb.WithName("Crazy").WithValue(gr.Crazy.ToString("F2")).WithIsInline(true))
|
||||
.AddField(efb => efb.WithName("Advice").WithValue(gr.Advice).WithIsInline(false))
|
||||
.WithImageUrl(img)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private double NextDouble(double x, double y)
|
||||
{
|
||||
var rng = new Random();
|
||||
return rng.NextDouble() * (y - x) + x;
|
||||
}
|
||||
|
||||
private GirlRating GetGirl(ulong uid)
|
||||
{
|
||||
var rng = new NadekoRandom();
|
||||
|
||||
var roll = rng.Next(1, 1001);
|
||||
|
||||
if ((uid == 185968432783687681 ||
|
||||
uid == 265642040950390784) && roll >= 900)
|
||||
roll = 1000;
|
||||
|
||||
|
||||
double hot;
|
||||
double crazy;
|
||||
string advice;
|
||||
if (roll < 500)
|
||||
{
|
||||
hot = NextDouble(0, 5);
|
||||
crazy = NextDouble(4, 10);
|
||||
advice =
|
||||
"This is your NO-GO ZONE. We do not hang around, and date, and marry women who are atleast, in our mind, a 5. " +
|
||||
"So, this is your no-go zone. You don't go here. You just rule this out. Life is better this way, that's the way it is.";
|
||||
}
|
||||
else if (roll < 750)
|
||||
{
|
||||
hot = NextDouble(5, 8);
|
||||
crazy = NextDouble(4, .6 * hot + 4);
|
||||
advice = "Above a 5, and to about an 8, and below the crazy line - this is your FUN ZONE. You can " +
|
||||
"hang around here, and meet these girls and spend time with them. Keep in mind, while you're " +
|
||||
"in the fun zone, you want to move OUT of the fun zone to a more permanent location. " +
|
||||
"These girls are most of the time not crazy.";
|
||||
}
|
||||
else if (roll < 900)
|
||||
{
|
||||
hot = NextDouble(5, 10);
|
||||
crazy = NextDouble(.61 * hot + 4, 10);
|
||||
advice = "Above the crazy line - it's the DANGER ZONE. This is redheads, strippers, anyone named Tiffany, " +
|
||||
"hairdressers... This is where your car gets keyed, you get bunny in the pot, your tires get slashed, " +
|
||||
"and you wind up in jail.";
|
||||
}
|
||||
else if (roll < 951)
|
||||
{
|
||||
hot = NextDouble(8, 10);
|
||||
crazy = NextDouble(7, .6 * hot + 4);
|
||||
advice = "Below the crazy line, above an 8 hot, but still about 7 crazy. This is your DATE ZONE. " +
|
||||
"You can stay in the date zone indefinitely. These are the girls you introduce to your friends and your family. " +
|
||||
"They're good looking, and they're reasonably not crazy most of the time. You can stay here indefinitely.";
|
||||
}
|
||||
else if (roll < 990)
|
||||
{
|
||||
hot = NextDouble(8, 10);
|
||||
crazy = NextDouble(5, 7);
|
||||
advice = "Above an 8 hot, and between about 7 and a 5 crazy - this is WIFE ZONE. You you meet this girl, you should consider long-term " +
|
||||
"relationship. Rare.";
|
||||
}
|
||||
else if (roll < 999)
|
||||
{
|
||||
hot = NextDouble(8, 10);
|
||||
crazy = NextDouble(2, 3.99d);
|
||||
advice = "You've met a girl she's above 8 hot, and not crazy at all (below 4)... totally cool?" +
|
||||
" You should be careful. That's a dude. It's a tranny.";
|
||||
}
|
||||
else
|
||||
{
|
||||
hot = NextDouble(8, 10);
|
||||
crazy = NextDouble(4, 5);
|
||||
advice = "Below 5 crazy, and above 8 hot, this is the UNICORN ZONE, these things don't exist." +
|
||||
"If you find a unicorn, please capture it safely, keep it alive, we'd like to study it, " +
|
||||
"and maybe look at how to replicate that.";
|
||||
}
|
||||
|
||||
return new GirlRating(crazy, hot, roll, advice);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task Linux(string guhnoo, string loonix)
|
||||
{
|
||||
|
@ -13,19 +13,26 @@ using System.Collections.Generic;
|
||||
namespace NadekoBot.Modules.Help
|
||||
{
|
||||
[NadekoModule("Help", "-")]
|
||||
public partial class Help : DiscordModule
|
||||
public class Help : NadekoTopLevelModule
|
||||
{
|
||||
private static string helpString { get; } = NadekoBot.BotConfig.HelpString;
|
||||
public static string HelpString => String.Format(helpString, NadekoBot.Credentials.ClientId, NadekoBot.ModulePrefixes[typeof(Help).Name]);
|
||||
|
||||
public static string DMHelpString { get; } = NadekoBot.BotConfig.DMHelpString;
|
||||
|
||||
public const string PatreonUrl = "https://patreon.com/nadekobot";
|
||||
public const string PaypalUrl = "https://paypal.me/Kwoth";
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task Modules()
|
||||
{
|
||||
|
||||
var embed = new EmbedBuilder().WithOkColor().WithFooter(efb => efb.WithText($" ℹ️ Type `-cmds ModuleName` to get a list of commands in that module. eg `-cmds games`"))
|
||||
.WithTitle("📜 List Of Modules").WithDescription("\n• " + string.Join("\n• ", NadekoBot.CommandService.Modules.GroupBy(m => m.GetTopLevelModule()).Select(m => m.Key.Name).OrderBy(s => s)));
|
||||
var embed = new EmbedBuilder().WithOkColor()
|
||||
.WithFooter(efb => efb.WithText("ℹ️" + GetText("modules_footer", Prefix)))
|
||||
.WithTitle(GetText("list_of_modules"))
|
||||
.WithDescription(string.Join("\n",
|
||||
NadekoBot.CommandService.Modules.GroupBy(m => m.GetTopLevelModule())
|
||||
.Select(m => "• " + m.Key.Name)
|
||||
.OrderBy(s => s)));
|
||||
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@ -45,18 +52,13 @@ namespace NadekoBot.Modules.Help
|
||||
var cmdsArray = cmds as CommandInfo[] ?? cmds.ToArray();
|
||||
if (!cmdsArray.Any())
|
||||
{
|
||||
await channel.SendErrorAsync("That module does not exist.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("module_not_found").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
if (module != "customreactions" && module != "conversations")
|
||||
{
|
||||
await channel.SendTableAsync("📃 **List Of Commands:**\n", cmdsArray, el => $"{el.Aliases.First(),-15} {"["+el.Aliases.Skip(1).FirstOrDefault()+"]",-8}").ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await channel.SendMessageAsync("📃 **List Of Commands:**\n• " + string.Join("\n• ", cmdsArray.Select(c => $"{c.Aliases.First()}")));
|
||||
}
|
||||
await channel.SendConfirmAsync($"ℹ️ **Type** `\"{NadekoBot.ModulePrefixes[typeof(Help).Name]}h CommandName\"` **to see the help for that specified command.** ***e.g.*** `-h >8ball`").ConfigureAwait(false);
|
||||
|
||||
await channel.SendTableAsync($"📃 **{GetText("list_of_commands")}**\n", cmdsArray, el => $"{el.Aliases.First(),-15} {"["+el.Aliases.Skip(1).FirstOrDefault()+"]",-8}").ConfigureAwait(false);
|
||||
|
||||
await ConfirmLocalized("commands_instr", Prefix).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -75,32 +77,33 @@ namespace NadekoBot.Modules.Help
|
||||
|
||||
if (com == null)
|
||||
{
|
||||
await channel.SendErrorAsync("I can't find that command. Please check the **command** and **command prefix** before trying again.");
|
||||
await ReplyErrorLocalized("command_not_found").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
var str = $"**`{com.Aliases.First()}`**";
|
||||
var str = string.Format("**`{0}`**", com.Aliases.First());
|
||||
var alias = com.Aliases.Skip(1).FirstOrDefault();
|
||||
if (alias != null)
|
||||
str += $" **/ `{alias}`**";
|
||||
var embed = new EmbedBuilder()
|
||||
.AddField(fb => fb.WithName(str).WithValue($"{ string.Format(com.Summary, com.Module.Aliases.First())} { GetCommandRequirements(com)}").WithIsInline(true))
|
||||
.AddField(fb => fb.WithName("**Usage**").WithValue($"{string.Format(com.Remarks, com.Module.Aliases.First())}").WithIsInline(false))
|
||||
str += string.Format(" **/ `{0}`**", alias);
|
||||
var embed = new EmbedBuilder()
|
||||
.AddField(fb => fb.WithName(str).WithValue($"{string.Format(com.Summary, com.Module.Aliases.First())} {GetCommandRequirements(com)}").WithIsInline(true))
|
||||
.AddField(fb => fb.WithName(GetText("usage")).WithValue(string.Format(com.Remarks, com.Module.Aliases.First())).WithIsInline(false))
|
||||
.WithColor(NadekoBot.OkColor);
|
||||
await channel.EmbedAsync(embed).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private string GetCommandRequirements(CommandInfo cmd) =>
|
||||
String.Join(" ", cmd.Preconditions
|
||||
string.Join(" ", cmd.Preconditions
|
||||
.Where(ca => ca is OwnerOnlyAttribute || ca is RequireUserPermissionAttribute)
|
||||
.Select(ca =>
|
||||
{
|
||||
if (ca is OwnerOnlyAttribute)
|
||||
return "**Bot Owner only.**";
|
||||
return Format.Bold(GetText("bot_owner_only"));
|
||||
var cau = (RequireUserPermissionAttribute)ca;
|
||||
if (cau.GuildPermission != null)
|
||||
return $"**Requires {cau.GuildPermission} server permission.**".Replace("Guild", "Server");
|
||||
else
|
||||
return $"**Requires {cau.ChannelPermission} channel permission.**".Replace("Guild", "Server");
|
||||
return Format.Bold(GetText("server_permission", cau.GuildPermission))
|
||||
.Replace("Guild", "Server");
|
||||
return Format.Bold(GetText("channel_permission", cau.ChannelPermission))
|
||||
.Replace("Guild", "Server");
|
||||
}));
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -109,14 +112,14 @@ namespace NadekoBot.Modules.Help
|
||||
public async Task Hgit()
|
||||
{
|
||||
var helpstr = new StringBuilder();
|
||||
helpstr.AppendLine("You can support the project on patreon: <https://patreon.com/nadekobot> or paypal: <https://www.paypal.me/Kwoth>\n");
|
||||
helpstr.AppendLine("##Table Of Contents");
|
||||
helpstr.AppendLine(GetText("cmdlist_donate", PatreonUrl, PaypalUrl) + "\n");
|
||||
helpstr.AppendLine("##"+ GetText("table_of_contents"));
|
||||
helpstr.AppendLine(string.Join("\n", NadekoBot.CommandService.Modules.Where(m => m.GetTopLevelModule().Name.ToLowerInvariant() != "help")
|
||||
.Select(m => m.GetTopLevelModule().Name)
|
||||
.Distinct()
|
||||
.OrderBy(m => m)
|
||||
.Prepend("Help")
|
||||
.Select(m => $"- [{m}](#{m.ToLowerInvariant()})")));
|
||||
.Select(m => string.Format("- [{0}](#{1})", m, m.ToLowerInvariant()))));
|
||||
helpstr.AppendLine();
|
||||
string lastModule = null;
|
||||
foreach (var com in NadekoBot.CommandService.Commands.OrderBy(com => com.Module.GetTopLevelModule().Name).GroupBy(c => c.Aliases.First()).Select(g => g.First()))
|
||||
@ -127,44 +130,35 @@ namespace NadekoBot.Modules.Help
|
||||
if (lastModule != null)
|
||||
{
|
||||
helpstr.AppendLine();
|
||||
helpstr.AppendLine("###### [Back to TOC](#table-of-contents)");
|
||||
helpstr.AppendLine($"###### [{GetText("back_to_toc")}](#{GetText("table_of_contents").ToLowerInvariant().Replace(' ', '-')})");
|
||||
}
|
||||
helpstr.AppendLine();
|
||||
helpstr.AppendLine("### " + module.Name + " ");
|
||||
helpstr.AppendLine("Command and aliases | Description | Usage");
|
||||
helpstr.AppendLine($"{GetText("cmd_and_alias")} | {GetText("desc")} | {GetText("usage")}");
|
||||
helpstr.AppendLine("----------------|--------------|-------");
|
||||
lastModule = module.Name;
|
||||
}
|
||||
helpstr.AppendLine($"{string.Join(" ", com.Aliases.Select(a => "`" + a + "`"))} | {string.Format(com.Summary, com.Module.GetPrefix())} {GetCommandRequirements(com)} | {string.Format(com.Remarks, com.Module.GetPrefix())}");
|
||||
helpstr.AppendLine($"{string.Join(" ", com.Aliases.Select(a => "`" + a + "`"))} |" +
|
||||
$" {string.Format(com.Summary, com.Module.GetPrefix())} {GetCommandRequirements(com)} |" +
|
||||
$" {string.Format(com.Remarks, com.Module.GetPrefix())}");
|
||||
}
|
||||
helpstr = helpstr.Replace(NadekoBot.Client.CurrentUser.Username , "@BotName");
|
||||
File.WriteAllText("../../docs/Commands List.md", helpstr.ToString());
|
||||
await Context.Channel.SendConfirmAsync("Commandlist Regenerated").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("commandlist_regen").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task Guide()
|
||||
{
|
||||
var channel = (ITextChannel)Context.Channel;
|
||||
|
||||
await channel.SendConfirmAsync(
|
||||
@"**LIST OF COMMANDS**: <http://nadekobot.readthedocs.io/en/latest/Commands%20List/>
|
||||
**Hosting Guides and docs can be found here**: <http://nadekobot.readthedocs.io/en/latest/>").ConfigureAwait(false);
|
||||
await ConfirmLocalized("guide",
|
||||
"http://nadekobot.readthedocs.io/en/latest/Commands%20List/",
|
||||
"http://nadekobot.readthedocs.io/en/latest/").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task Donate()
|
||||
{
|
||||
var channel = (ITextChannel)Context.Channel;
|
||||
|
||||
await channel.SendConfirmAsync(
|
||||
$@"You can support the NadekoBot project on patreon. <https://patreon.com/nadekobot> or
|
||||
Paypal <https://paypal.me/Kwoth>
|
||||
Don't forget to leave your discord name or id in the message.
|
||||
|
||||
**Thank you** ♥️").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("donate", PatreonUrl, PaypalUrl).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,8 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using NLog;
|
||||
|
||||
namespace NadekoBot.Modules.Music.Classes
|
||||
{
|
||||
|
||||
@ -46,17 +48,19 @@ namespace NadekoBot.Modules.Music.Classes
|
||||
|
||||
// this should be written better
|
||||
public TimeSpan TotalPlaytime =>
|
||||
playlist.Any(s => s.TotalTime == TimeSpan.MaxValue) ?
|
||||
_playlist.Any(s => s.TotalTime == TimeSpan.MaxValue) ?
|
||||
TimeSpan.MaxValue :
|
||||
new TimeSpan(playlist.Sum(s => s.TotalTime.Ticks));
|
||||
new TimeSpan(_playlist.Sum(s => s.TotalTime.Ticks));
|
||||
|
||||
/// <summary>
|
||||
/// Users who recently got their music wish
|
||||
/// </summary>
|
||||
private ConcurrentHashSet<string> recentlyPlayedUsers { get; } = new ConcurrentHashSet<string>();
|
||||
|
||||
private readonly List<Song> playlist = new List<Song>();
|
||||
public IReadOnlyCollection<Song> Playlist => playlist;
|
||||
private readonly List<Song> _playlist = new List<Song>();
|
||||
private readonly Logger _log;
|
||||
|
||||
public IReadOnlyCollection<Song> Playlist => _playlist;
|
||||
|
||||
public Song CurrentSong { get; private set; }
|
||||
public CancellationTokenSource SongCancelSource { get; private set; }
|
||||
@ -67,16 +71,16 @@ namespace NadekoBot.Modules.Music.Classes
|
||||
public float Volume { get; private set; }
|
||||
|
||||
public event Action<MusicPlayer, Song> OnCompleted = delegate { };
|
||||
public event Action<MusicPlayer, Song> OnStarted = delegate { };
|
||||
public event Action<MusicPlayer, Song> OnStarted = delegate { };
|
||||
public event Action<bool> OnPauseChanged = delegate { };
|
||||
|
||||
public IVoiceChannel PlaybackVoiceChannel { get; private set; }
|
||||
public ITextChannel OutputTextChannel { get; set; }
|
||||
|
||||
private bool Destroyed { get; set; } = false;
|
||||
public bool RepeatSong { get; private set; } = false;
|
||||
public bool RepeatPlaylist { get; private set; } = false;
|
||||
public bool Autoplay { get; set; } = false;
|
||||
private bool destroyed { get; set; }
|
||||
public bool RepeatSong { get; private set; }
|
||||
public bool RepeatPlaylist { get; private set; }
|
||||
public bool Autoplay { get; set; }
|
||||
public uint MaxQueueSize { get; set; } = 0;
|
||||
|
||||
private ConcurrentQueue<Action> actionQueue { get; } = new ConcurrentQueue<Action>();
|
||||
@ -90,6 +94,8 @@ namespace NadekoBot.Modules.Music.Classes
|
||||
if (startingVoiceChannel == null)
|
||||
throw new ArgumentNullException(nameof(startingVoiceChannel));
|
||||
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
OutputTextChannel = outputChannel;
|
||||
Volume = defaultVolume ?? 1.0f;
|
||||
|
||||
@ -101,7 +107,7 @@ namespace NadekoBot.Modules.Music.Classes
|
||||
{
|
||||
try
|
||||
{
|
||||
while (!Destroyed)
|
||||
while (!destroyed)
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -119,31 +125,27 @@ namespace NadekoBot.Modules.Music.Classes
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("Action queue crashed");
|
||||
Console.WriteLine(ex);
|
||||
_log.Warn("Action queue crashed");
|
||||
_log.Warn(ex);
|
||||
}
|
||||
}).ConfigureAwait(false);
|
||||
|
||||
var t = new Thread(new ThreadStart(async () =>
|
||||
var t = new Thread(async () =>
|
||||
{
|
||||
while (!Destroyed)
|
||||
while (!destroyed)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (audioClient?.ConnectionState != ConnectionState.Connected)
|
||||
{
|
||||
if (audioClient != null)
|
||||
try { await audioClient.DisconnectAsync().ConfigureAwait(false); } catch { }
|
||||
audioClient = await PlaybackVoiceChannel.ConnectAsync().ConfigureAwait(false);
|
||||
continue;
|
||||
}
|
||||
|
||||
CurrentSong = GetNextSong();
|
||||
|
||||
if (CurrentSong == null)
|
||||
continue;
|
||||
|
||||
var index = playlist.IndexOf(CurrentSong);
|
||||
if (audioClient != null)
|
||||
try { await audioClient.DisconnectAsync().ConfigureAwait(false); } catch { }
|
||||
audioClient = await PlaybackVoiceChannel.ConnectAsync().ConfigureAwait(false);
|
||||
|
||||
var index = _playlist.IndexOf(CurrentSong);
|
||||
if (index != -1)
|
||||
RemoveSongAt(index, true);
|
||||
|
||||
@ -152,11 +154,14 @@ namespace NadekoBot.Modules.Music.Classes
|
||||
{
|
||||
await CurrentSong.Play(audioClient, cancelToken);
|
||||
}
|
||||
catch(OperationCanceledException)
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
}
|
||||
finally
|
||||
{
|
||||
OnCompleted(this, CurrentSong);
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (RepeatPlaylist)
|
||||
AddSong(CurrentSong, CurrentSong.QueuerName);
|
||||
@ -167,8 +172,8 @@ namespace NadekoBot.Modules.Music.Classes
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("Music thread almost crashed.");
|
||||
Console.WriteLine(ex);
|
||||
_log.Warn("Music thread almost crashed.");
|
||||
_log.Warn(ex);
|
||||
await Task.Delay(3000).ConfigureAwait(false);
|
||||
}
|
||||
finally
|
||||
@ -183,7 +188,7 @@ namespace NadekoBot.Modules.Music.Classes
|
||||
await Task.Delay(300).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}));
|
||||
});
|
||||
|
||||
t.Start();
|
||||
}
|
||||
@ -203,7 +208,8 @@ namespace NadekoBot.Modules.Music.Classes
|
||||
{
|
||||
RepeatPlaylist = false;
|
||||
RepeatSong = false;
|
||||
playlist.Clear();
|
||||
Autoplay = false;
|
||||
_playlist.Clear();
|
||||
if (!SongCancelSource.IsCancellationRequested)
|
||||
SongCancelSource.Cancel();
|
||||
});
|
||||
@ -226,10 +232,10 @@ namespace NadekoBot.Modules.Music.Classes
|
||||
{
|
||||
if (!FairPlay)
|
||||
{
|
||||
return playlist.FirstOrDefault();
|
||||
return _playlist.FirstOrDefault();
|
||||
}
|
||||
var song = playlist.FirstOrDefault(c => !recentlyPlayedUsers.Contains(c.QueuerName))
|
||||
?? playlist.FirstOrDefault();
|
||||
var song = _playlist.FirstOrDefault(c => !recentlyPlayedUsers.Contains(c.QueuerName))
|
||||
?? _playlist.FirstOrDefault();
|
||||
|
||||
if (song == null)
|
||||
return null;
|
||||
@ -247,9 +253,9 @@ namespace NadekoBot.Modules.Music.Classes
|
||||
{
|
||||
actionQueue.Enqueue(() =>
|
||||
{
|
||||
var oldPlaylist = playlist.ToArray();
|
||||
playlist.Clear();
|
||||
playlist.AddRange(oldPlaylist.Shuffle());
|
||||
var oldPlaylist = _playlist.ToArray();
|
||||
_playlist.Clear();
|
||||
_playlist.AddRange(oldPlaylist.Shuffle());
|
||||
});
|
||||
}
|
||||
|
||||
@ -262,7 +268,7 @@ namespace NadekoBot.Modules.Music.Classes
|
||||
{
|
||||
s.MusicPlayer = this;
|
||||
s.QueuerName = username.TrimTo(10);
|
||||
playlist.Add(s);
|
||||
_playlist.Add(s);
|
||||
});
|
||||
}
|
||||
|
||||
@ -272,7 +278,7 @@ namespace NadekoBot.Modules.Music.Classes
|
||||
throw new ArgumentNullException(nameof(s));
|
||||
actionQueue.Enqueue(() =>
|
||||
{
|
||||
playlist.Insert(index, s);
|
||||
_playlist.Insert(index, s);
|
||||
});
|
||||
}
|
||||
|
||||
@ -282,7 +288,7 @@ namespace NadekoBot.Modules.Music.Classes
|
||||
throw new ArgumentNullException(nameof(s));
|
||||
actionQueue.Enqueue(() =>
|
||||
{
|
||||
playlist.Remove(s);
|
||||
_playlist.Remove(s);
|
||||
});
|
||||
}
|
||||
|
||||
@ -290,10 +296,10 @@ namespace NadekoBot.Modules.Music.Classes
|
||||
{
|
||||
actionQueue.Enqueue(() =>
|
||||
{
|
||||
if (index < 0 || index >= playlist.Count)
|
||||
if (index < 0 || index >= _playlist.Count)
|
||||
return;
|
||||
var song = playlist.ElementAtOrDefault(index);
|
||||
if (playlist.Remove(song) && !silent)
|
||||
var song = _playlist.ElementAtOrDefault(index);
|
||||
if (_playlist.Remove(song) && !silent)
|
||||
{
|
||||
SongRemoved(song, index);
|
||||
}
|
||||
@ -305,19 +311,23 @@ namespace NadekoBot.Modules.Music.Classes
|
||||
{
|
||||
actionQueue.Enqueue(() =>
|
||||
{
|
||||
playlist.Clear();
|
||||
_playlist.Clear();
|
||||
});
|
||||
}
|
||||
|
||||
public async Task UpdateSongDurationsAsync()
|
||||
{
|
||||
var curSong = CurrentSong;
|
||||
var toUpdate = playlist.Where(s => s.SongInfo.ProviderType == MusicType.Normal &&
|
||||
s.TotalTime == TimeSpan.Zero);
|
||||
var toUpdate = _playlist.Where(s => s.SongInfo.ProviderType == MusicType.Normal &&
|
||||
s.TotalTime == TimeSpan.Zero)
|
||||
.ToArray();
|
||||
if (curSong != null)
|
||||
toUpdate = toUpdate.Append(curSong);
|
||||
{
|
||||
Array.Resize(ref toUpdate, toUpdate.Length + 1);
|
||||
toUpdate[toUpdate.Length - 1] = curSong;
|
||||
}
|
||||
var ids = toUpdate.Select(s => s.SongInfo.Query.Substring(s.SongInfo.Query.LastIndexOf("?v=") + 3))
|
||||
.Distinct();
|
||||
.Distinct();
|
||||
|
||||
var durations = await NadekoBot.Google.GetVideoDurationsAsync(ids);
|
||||
|
||||
@ -341,8 +351,9 @@ namespace NadekoBot.Modules.Music.Classes
|
||||
{
|
||||
RepeatPlaylist = false;
|
||||
RepeatSong = false;
|
||||
Destroyed = true;
|
||||
playlist.Clear();
|
||||
Autoplay = false;
|
||||
destroyed = true;
|
||||
_playlist.Clear();
|
||||
|
||||
try { await audioClient.DisconnectAsync(); } catch { }
|
||||
if (!SongCancelSource.IsCancellationRequested)
|
||||
@ -358,17 +369,17 @@ namespace NadekoBot.Modules.Music.Classes
|
||||
// audioClient = await voiceChannel.ConnectAsync().ConfigureAwait(false);
|
||||
//}
|
||||
|
||||
public bool ToggleRepeatSong() => this.RepeatSong = !this.RepeatSong;
|
||||
public bool ToggleRepeatSong() => RepeatSong = !RepeatSong;
|
||||
|
||||
public bool ToggleRepeatPlaylist() => this.RepeatPlaylist = !this.RepeatPlaylist;
|
||||
public bool ToggleRepeatPlaylist() => RepeatPlaylist = !RepeatPlaylist;
|
||||
|
||||
public bool ToggleAutoplay() => this.Autoplay = !this.Autoplay;
|
||||
public bool ToggleAutoplay() => Autoplay = !Autoplay;
|
||||
|
||||
public void ThrowIfQueueFull()
|
||||
{
|
||||
if (MaxQueueSize == 0)
|
||||
return;
|
||||
if (playlist.Count >= MaxQueueSize)
|
||||
if (_playlist.Count >= MaxQueueSize)
|
||||
throw new PlaylistFullException();
|
||||
}
|
||||
}
|
||||
|
@ -5,12 +5,9 @@ using System;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using VideoLibrary;
|
||||
using System.Net;
|
||||
|
||||
namespace NadekoBot.Modules.Music.Classes
|
||||
@ -32,22 +29,22 @@ namespace NadekoBot.Modules.Music.Classes
|
||||
public string QueuerName { get; set; }
|
||||
|
||||
public TimeSpan TotalTime { get; set; } = TimeSpan.Zero;
|
||||
public TimeSpan CurrentTime => TimeSpan.FromSeconds(bytesSent / frameBytes / (1000 / milliseconds));
|
||||
public TimeSpan CurrentTime => TimeSpan.FromSeconds(bytesSent / (float)_frameBytes / (1000 / (float)_milliseconds));
|
||||
|
||||
const int milliseconds = 20;
|
||||
const int samplesPerFrame = (48000 / 1000) * milliseconds;
|
||||
const int frameBytes = 3840; //16-bit, 2 channels
|
||||
private const int _milliseconds = 20;
|
||||
private const int _samplesPerFrame = (48000 / 1000) * _milliseconds;
|
||||
private const int _frameBytes = 3840; //16-bit, 2 channels
|
||||
|
||||
private ulong bytesSent { get; set; } = 0;
|
||||
private ulong bytesSent { get; set; }
|
||||
|
||||
//pwetty
|
||||
|
||||
public string PrettyProvider =>
|
||||
$"{(SongInfo.Provider ?? "No Provider")}";
|
||||
$"{(SongInfo.Provider ?? "???")}";
|
||||
|
||||
public string PrettyFullTime => PrettyCurrentTime + " / " + PrettyTotalTime;
|
||||
|
||||
public string PrettyName => $"**[{SongInfo.Title.TrimTo(65)}]({songUrl})**";
|
||||
public string PrettyName => $"**[{SongInfo.Title.TrimTo(65)}]({SongUrl})**";
|
||||
|
||||
public string PrettyInfo => $"{MusicPlayer.PrettyVolume} | {PrettyTotalTime} | {PrettyProvider} | {QueuerName}";
|
||||
|
||||
@ -65,22 +62,19 @@ namespace NadekoBot.Modules.Music.Classes
|
||||
}
|
||||
}
|
||||
|
||||
private string PrettyTotalTime {
|
||||
get {
|
||||
public string PrettyTotalTime {
|
||||
get
|
||||
{
|
||||
if (TotalTime == TimeSpan.Zero)
|
||||
return "(?)";
|
||||
else if (TotalTime == TimeSpan.MaxValue)
|
||||
if (TotalTime == TimeSpan.MaxValue)
|
||||
return "∞";
|
||||
else
|
||||
{
|
||||
var time = TotalTime.ToString(@"mm\:ss");
|
||||
var hrs = (int)TotalTime.TotalHours;
|
||||
var time = TotalTime.ToString(@"mm\:ss");
|
||||
var hrs = (int)TotalTime.TotalHours;
|
||||
|
||||
if (hrs > 0)
|
||||
return hrs + ":" + time;
|
||||
else
|
||||
return time;
|
||||
}
|
||||
if (hrs > 0)
|
||||
return hrs + ":" + time;
|
||||
return time;
|
||||
}
|
||||
}
|
||||
|
||||
@ -89,13 +83,13 @@ namespace NadekoBot.Modules.Music.Classes
|
||||
switch (SongInfo.ProviderType)
|
||||
{
|
||||
case MusicType.Radio:
|
||||
return $"https://cdn.discordapp.com/attachments/155726317222887425/261850925063340032/1482522097_radio.png"; //test links
|
||||
return "https://cdn.discordapp.com/attachments/155726317222887425/261850925063340032/1482522097_radio.png"; //test links
|
||||
case MusicType.Normal:
|
||||
//todo have videoid in songinfo from the start
|
||||
var videoId = Regex.Match(SongInfo.Query, "<=v=[a-zA-Z0-9-]+(?=&)|(?<=[0-9])[^&\n]+|(?<=v=)[^&\n]+");
|
||||
return $"https://img.youtube.com/vi/{ videoId }/0.jpg";
|
||||
case MusicType.Local:
|
||||
return $"https://cdn.discordapp.com/attachments/155726317222887425/261850914783100928/1482522077_music.png"; //test links
|
||||
return "https://cdn.discordapp.com/attachments/155726317222887425/261850914783100928/1482522077_music.png"; //test links
|
||||
case MusicType.Soundcloud:
|
||||
return SongInfo.AlbumArt;
|
||||
default:
|
||||
@ -104,7 +98,7 @@ namespace NadekoBot.Modules.Music.Classes
|
||||
}
|
||||
}
|
||||
|
||||
private string songUrl {
|
||||
public string SongUrl {
|
||||
get {
|
||||
switch (SongInfo.ProviderType)
|
||||
{
|
||||
@ -122,36 +116,32 @@ namespace NadekoBot.Modules.Music.Classes
|
||||
}
|
||||
}
|
||||
|
||||
private int skipTo = 0;
|
||||
public int SkipTo {
|
||||
get { return skipTo; }
|
||||
set {
|
||||
skipTo = value;
|
||||
bytesSent = (ulong)skipTo * 3840 * 50;
|
||||
}
|
||||
}
|
||||
public int SkipTo { get; set; }
|
||||
|
||||
private readonly Logger _log;
|
||||
|
||||
public Song(SongInfo songInfo)
|
||||
{
|
||||
this.SongInfo = songInfo;
|
||||
this._log = LogManager.GetCurrentClassLogger();
|
||||
SongInfo = songInfo;
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
}
|
||||
|
||||
public Song Clone()
|
||||
{
|
||||
var s = new Song(SongInfo);
|
||||
s.MusicPlayer = MusicPlayer;
|
||||
s.QueuerName = QueuerName;
|
||||
var s = new Song(SongInfo)
|
||||
{
|
||||
MusicPlayer = MusicPlayer,
|
||||
QueuerName = QueuerName
|
||||
};
|
||||
return s;
|
||||
}
|
||||
|
||||
public async Task Play(IAudioClient voiceClient, CancellationToken cancelToken)
|
||||
{
|
||||
bytesSent = (ulong) SkipTo * 3840 * 50;
|
||||
var filename = Path.Combine(Music.MusicDataPath, DateTime.Now.UnixTimestamp().ToString());
|
||||
|
||||
SongBuffer inStream = new SongBuffer(MusicPlayer, filename, SongInfo, skipTo, frameBytes * 100);
|
||||
var inStream = new SongBuffer(MusicPlayer, filename, SongInfo, SkipTo, _frameBytes * 100);
|
||||
var bufferTask = inStream.BufferSong(cancelToken).ConfigureAwait(false);
|
||||
|
||||
try
|
||||
@ -200,22 +190,22 @@ namespace NadekoBot.Modules.Music.Classes
|
||||
|
||||
var outStream = voiceClient.CreatePCMStream(960);
|
||||
|
||||
int nextTime = Environment.TickCount + milliseconds;
|
||||
int nextTime = Environment.TickCount + _milliseconds;
|
||||
|
||||
byte[] buffer = new byte[frameBytes];
|
||||
byte[] buffer = new byte[_frameBytes];
|
||||
while (!cancelToken.IsCancellationRequested && //song canceled for whatever reason
|
||||
!(MusicPlayer.MaxPlaytimeSeconds != 0 && CurrentTime.TotalSeconds >= MusicPlayer.MaxPlaytimeSeconds)) // or exceedded max playtime
|
||||
{
|
||||
//Console.WriteLine($"Read: {songBuffer.ReadPosition}\nWrite: {songBuffer.WritePosition}\nContentLength:{songBuffer.ContentLength}\n---------");
|
||||
var read = await inStream.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false);
|
||||
//await inStream.CopyToAsync(voiceClient.OutputStream);
|
||||
if (read < frameBytes)
|
||||
if (read < _frameBytes)
|
||||
_log.Debug("read {0}", read);
|
||||
unchecked
|
||||
{
|
||||
bytesSent += (ulong)read;
|
||||
}
|
||||
if (read < frameBytes)
|
||||
if (read < _frameBytes)
|
||||
{
|
||||
if (read == 0)
|
||||
{
|
||||
@ -231,12 +221,12 @@ namespace NadekoBot.Modules.Music.Classes
|
||||
_log.Warn("Slow connection has disrupted music, waiting a bit for buffer");
|
||||
|
||||
await Task.Delay(1000, cancelToken).ConfigureAwait(false);
|
||||
nextTime = Environment.TickCount + milliseconds;
|
||||
nextTime = Environment.TickCount + _milliseconds;
|
||||
}
|
||||
else
|
||||
{
|
||||
await Task.Delay(100, cancelToken).ConfigureAwait(false);
|
||||
nextTime = Environment.TickCount + milliseconds;
|
||||
nextTime = Environment.TickCount + _milliseconds;
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -245,16 +235,16 @@ namespace NadekoBot.Modules.Music.Classes
|
||||
else
|
||||
attempt = 0;
|
||||
|
||||
while (this.MusicPlayer.Paused)
|
||||
while (MusicPlayer.Paused)
|
||||
{
|
||||
await Task.Delay(200, cancelToken).ConfigureAwait(false);
|
||||
nextTime = Environment.TickCount + milliseconds;
|
||||
nextTime = Environment.TickCount + _milliseconds;
|
||||
}
|
||||
|
||||
|
||||
buffer = AdjustVolume(buffer, MusicPlayer.Volume);
|
||||
if (read != frameBytes) continue;
|
||||
nextTime = unchecked(nextTime + milliseconds);
|
||||
if (read != _frameBytes) continue;
|
||||
nextTime = unchecked(nextTime + _milliseconds);
|
||||
int delayMillis = unchecked(nextTime - Environment.TickCount);
|
||||
if (delayMillis > 0)
|
||||
await Task.Delay(delayMillis, cancelToken).ConfigureAwait(false);
|
||||
@ -264,8 +254,7 @@ namespace NadekoBot.Modules.Music.Classes
|
||||
finally
|
||||
{
|
||||
await bufferTask;
|
||||
if (inStream != null)
|
||||
inStream.Dispose();
|
||||
inStream.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@ -279,25 +268,20 @@ namespace NadekoBot.Modules.Music.Classes
|
||||
}
|
||||
|
||||
//aidiakapi ftw
|
||||
public unsafe static byte[] AdjustVolume(byte[] audioSamples, float volume)
|
||||
public static unsafe byte[] AdjustVolume(byte[] audioSamples, float volume)
|
||||
{
|
||||
Contract.Requires(audioSamples != null);
|
||||
Contract.Requires(audioSamples.Length % 2 == 0);
|
||||
Contract.Requires(volume >= 0f && volume <= 1f);
|
||||
Contract.Assert(BitConverter.IsLittleEndian);
|
||||
|
||||
if (Math.Abs(volume - 1f) < 0.0001f) return audioSamples;
|
||||
|
||||
// 16-bit precision for the multiplication
|
||||
int volumeFixed = (int)Math.Round(volume * 65536d);
|
||||
var volumeFixed = (int)Math.Round(volume * 65536d);
|
||||
|
||||
int count = audioSamples.Length / 2;
|
||||
var count = audioSamples.Length / 2;
|
||||
|
||||
fixed (byte* srcBytes = audioSamples)
|
||||
{
|
||||
short* src = (short*)srcBytes;
|
||||
var src = (short*)srcBytes;
|
||||
|
||||
for (int i = count; i != 0; i--, src++)
|
||||
for (var i = count; i != 0; i--, src++)
|
||||
*src = (short)(((*src) * volumeFixed) >> 16);
|
||||
}
|
||||
|
||||
|
@ -56,7 +56,7 @@ namespace NadekoBot.Modules.Music.Classes
|
||||
p = Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = "ffmpeg",
|
||||
Arguments = $"-ss {SkipTo} -i {SongInfo.Uri} -f s16le -ar 48000 -ac 2 pipe:1 -loglevel quiet",
|
||||
Arguments = $"-ss {SkipTo} -i {SongInfo.Uri} -f s16le -ar 48000 -vn -ac 2 pipe:1 -loglevel quiet",
|
||||
UseShellExecute = false,
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = false,
|
||||
|
@ -6,12 +6,14 @@ using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using NLog;
|
||||
using VideoLibrary;
|
||||
|
||||
namespace NadekoBot.Modules.Music.Classes
|
||||
{
|
||||
public static class SongHandler
|
||||
{
|
||||
private static readonly Logger _log = LogManager.GetCurrentClassLogger();
|
||||
public static async Task<Song> ResolveSong(string query, MusicType musicType = MusicType.Normal)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(query))
|
||||
@ -106,7 +108,8 @@ namespace NadekoBot.Modules.Music.Classes
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Failed resolving the link.{ex.Message}");
|
||||
_log.Warn($"Failed resolving the link.{ex.Message}");
|
||||
_log.Warn(ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -137,7 +140,7 @@ namespace NadekoBot.Modules.Music.Classes
|
||||
}
|
||||
catch
|
||||
{
|
||||
Console.WriteLine($"Failed reading .pls:\n{file}");
|
||||
_log.Warn($"Failed reading .pls:\n{file}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -156,7 +159,7 @@ namespace NadekoBot.Modules.Music.Classes
|
||||
}
|
||||
catch
|
||||
{
|
||||
Console.WriteLine($"Failed reading .m3u:\n{file}");
|
||||
_log.Warn($"Failed reading .m3u:\n{file}");
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -172,7 +175,7 @@ namespace NadekoBot.Modules.Music.Classes
|
||||
}
|
||||
catch
|
||||
{
|
||||
Console.WriteLine($"Failed reading .asx:\n{file}");
|
||||
_log.Warn($"Failed reading .asx:\n{file}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -192,7 +195,7 @@ namespace NadekoBot.Modules.Music.Classes
|
||||
}
|
||||
catch
|
||||
{
|
||||
Console.WriteLine($"Failed reading .xspf:\n{file}");
|
||||
_log.Warn($"Failed reading .xspf:\n{file}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -14,14 +14,13 @@ using System.Net.Http;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System.Collections.Generic;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
|
||||
namespace NadekoBot.Modules.Music
|
||||
{
|
||||
[NadekoModule("Music", "!!")]
|
||||
[DontAutoLoad]
|
||||
public partial class Music : DiscordModule
|
||||
public class Music : NadekoTopLevelModule
|
||||
{
|
||||
public static ConcurrentDictionary<ulong, MusicPlayer> MusicPlayers { get; } = new ConcurrentDictionary<ulong, MusicPlayer>();
|
||||
|
||||
@ -78,7 +77,10 @@ namespace NadekoBot.Modules.Music
|
||||
}
|
||||
|
||||
}
|
||||
catch { }
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
@ -153,7 +155,14 @@ namespace NadekoBot.Modules.Music
|
||||
return;
|
||||
var val = musicPlayer.FairPlay = !musicPlayer.FairPlay;
|
||||
|
||||
await channel.SendConfirmAsync("Fair play " + (val ? "enabled" : "disabled") + ".").ConfigureAwait(false);
|
||||
if (val)
|
||||
{
|
||||
await ReplyConfirmLocalized("fp_enabled").ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyConfirmLocalized("fp_disabled").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -182,57 +191,56 @@ namespace NadekoBot.Modules.Music
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task ListQueue(int page = 1)
|
||||
{
|
||||
|
||||
Song currentSong;
|
||||
MusicPlayer musicPlayer;
|
||||
if (!MusicPlayers.TryGetValue(Context.Guild.Id, out musicPlayer))
|
||||
if (!MusicPlayers.TryGetValue(Context.Guild.Id, out musicPlayer) ||
|
||||
(currentSong = musicPlayer?.CurrentSong) == null)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("🎵 No active music player.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("no_player").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
if (page <= 0)
|
||||
return;
|
||||
|
||||
var currentSong = musicPlayer.CurrentSong;
|
||||
if (currentSong == null)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("🎵 No active music player.").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
try { await musicPlayer.UpdateSongDurationsAsync().ConfigureAwait(false); } catch { }
|
||||
|
||||
const int itemsPerPage = 10;
|
||||
|
||||
var total = musicPlayer.TotalPlaytime;
|
||||
var totalStr = total == TimeSpan.MaxValue ? "∞" : $"{(int)total.TotalHours}h {total.Minutes}m {total.Seconds}s";
|
||||
var totalStr = total == TimeSpan.MaxValue ? "∞" : GetText("time_format",
|
||||
(int) total.TotalHours,
|
||||
total.Minutes,
|
||||
total.Seconds);
|
||||
var maxPlaytime = musicPlayer.MaxPlaytimeSeconds;
|
||||
var lastPage = musicPlayer.Playlist.Count / itemsPerPage;
|
||||
Func<int, EmbedBuilder> printAction = (curPage) =>
|
||||
Func<int, EmbedBuilder> printAction = curPage =>
|
||||
{
|
||||
int startAt = itemsPerPage * (curPage - 1);
|
||||
var startAt = itemsPerPage * (curPage - 1);
|
||||
var number = 0 + startAt;
|
||||
var desc = string.Join("\n", musicPlayer.Playlist
|
||||
.Skip(startAt)
|
||||
.Take(itemsPerPage)
|
||||
.Select(v => $"`{++number}.` {v.PrettyFullName}"));
|
||||
|
||||
if (currentSong != null)
|
||||
desc = $"`🔊` {currentSong.PrettyFullName}\n\n" + desc;
|
||||
|
||||
desc = $"`🔊` {currentSong.PrettyFullName}\n\n" + desc;
|
||||
|
||||
if (musicPlayer.RepeatSong)
|
||||
desc = "🔂 Repeating Current Song\n\n" + desc;
|
||||
desc = "🔂 " + GetText("repeating_cur_song") +"\n\n" + desc;
|
||||
else if (musicPlayer.RepeatPlaylist)
|
||||
desc = "🔁 Repeating Playlist\n\n" + desc;
|
||||
|
||||
desc = "🔁 " + GetText("repeating_playlist")+"\n\n" + desc;
|
||||
|
||||
|
||||
|
||||
var embed = new EmbedBuilder()
|
||||
.WithAuthor(eab => eab.WithName($"Player Queue - Page {curPage}/{lastPage + 1}")
|
||||
.WithMusicIcon())
|
||||
.WithAuthor(eab => eab.WithName(GetText("player_queue", curPage, lastPage + 1))
|
||||
.WithMusicIcon())
|
||||
.WithDescription(desc)
|
||||
.WithFooter(ef => ef.WithText($"{musicPlayer.PrettyVolume} | {musicPlayer.Playlist.Count} " +
|
||||
$"{("tracks".SnPl(musicPlayer.Playlist.Count))} | {totalStr} | " +
|
||||
(musicPlayer.FairPlay ? "✔️fairplay" : "✖️fairplay") + $" | " + (maxPlaytime == 0 ? "unlimited" : $"{maxPlaytime}s limit")))
|
||||
$"{("tracks".SnPl(musicPlayer.Playlist.Count))} | {totalStr} | " +
|
||||
(musicPlayer.FairPlay
|
||||
? "✔️" + GetText("fairplay")
|
||||
: "✖️" + GetText("fairplay")) + " | " +
|
||||
(maxPlaytime == 0 ? "unlimited" : GetText("play_limit", maxPlaytime))))
|
||||
.WithOkColor();
|
||||
|
||||
return embed;
|
||||
@ -253,7 +261,7 @@ namespace NadekoBot.Modules.Music
|
||||
try { await musicPlayer.UpdateSongDurationsAsync().ConfigureAwait(false); } catch { }
|
||||
|
||||
var embed = new EmbedBuilder().WithOkColor()
|
||||
.WithAuthor(eab => eab.WithName("Now Playing").WithMusicIcon())
|
||||
.WithAuthor(eab => eab.WithName(GetText("now_playing")).WithMusicIcon())
|
||||
.WithDescription(currentSong.PrettyName)
|
||||
.WithThumbnailUrl(currentSong.Thumbnail)
|
||||
.WithFooter(ef => ef.WithText(musicPlayer.PrettyVolume + " | " + currentSong.PrettyFullTime + $" | {currentSong.PrettyProvider} | {currentSong.QueuerName}"));
|
||||
@ -270,21 +278,22 @@ namespace NadekoBot.Modules.Music
|
||||
return;
|
||||
if (((IGuildUser)Context.User).VoiceChannel != musicPlayer.PlaybackVoiceChannel)
|
||||
return;
|
||||
if (val < 0)
|
||||
if (val < 0 || val > 100)
|
||||
{
|
||||
await ReplyErrorLocalized("volume_input_invalid").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
var volume = musicPlayer.SetVolume(val);
|
||||
await Context.Channel.SendConfirmAsync($"🎵 Volume set to {volume}%").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("volume_set", volume).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task Defvol([Remainder] int val)
|
||||
{
|
||||
|
||||
|
||||
if (val < 0 || val > 100)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("Volume number invalid. Must be between 0 and 100").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("volume_input_invalid").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
@ -292,7 +301,7 @@ namespace NadekoBot.Modules.Music
|
||||
uow.GuildConfigs.For(Context.Guild.Id, set => set).DefaultMusicVolume = val / 100.0f;
|
||||
uow.Complete();
|
||||
}
|
||||
await Context.Channel.SendConfirmAsync($"🎵 Default volume set to {val}%").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("defvol_set", val).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -306,13 +315,10 @@ namespace NadekoBot.Modules.Music
|
||||
if (((IGuildUser)Context.User).VoiceChannel != musicPlayer.PlaybackVoiceChannel)
|
||||
return;
|
||||
if (musicPlayer.Playlist.Count < 2)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("💢 Not enough songs in order to perform the shuffle.").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
musicPlayer.Shuffle();
|
||||
await Context.Channel.SendConfirmAsync("🎵 Songs shuffled.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("songs_shuffled").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -325,29 +331,31 @@ namespace NadekoBot.Modules.Music
|
||||
return;
|
||||
if (((IGuildUser)Context.User).VoiceChannel?.Guild != Context.Guild)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync($"💢 You need to be in a **voice channel** on this server.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("must_be_in_voice").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
var plId = (await NadekoBot.Google.GetPlaylistIdsByKeywordsAsync(arg).ConfigureAwait(false)).FirstOrDefault();
|
||||
if (plId == null)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("No search results for that query.");
|
||||
await ReplyErrorLocalized("no_search_results").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
var ids = await NadekoBot.Google.GetPlaylistTracksAsync(plId, 500).ConfigureAwait(false);
|
||||
if (!ids.Any())
|
||||
{
|
||||
await Context.Channel.SendErrorAsync($"🎵 Failed to find any songs.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("no_search_results").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
var count = ids.Count();
|
||||
|
||||
var msg = await Context.Channel.SendMessageAsync($"🎵 Attempting to queue **{count}** songs".SnPl(count) + "...").ConfigureAwait(false);
|
||||
var msg = await Context.Channel.SendMessageAsync(GetText("attempting_to_queue",
|
||||
Format.Bold(count.ToString())))
|
||||
.ConfigureAwait(false);
|
||||
|
||||
var cancelSource = new CancellationTokenSource();
|
||||
|
||||
var gusr = (IGuildUser)Context.User;
|
||||
|
||||
//todo use grouping
|
||||
while (ids.Any() && !cancelSource.IsCancellationRequested)
|
||||
{
|
||||
var tasks = Task.WhenAll(ids.Take(5).Select(async id =>
|
||||
@ -366,7 +374,7 @@ namespace NadekoBot.Modules.Music
|
||||
ids = ids.Skip(5);
|
||||
}
|
||||
|
||||
await msg.ModifyAsync(m => m.Content = "✅ Playlist queue complete.").ConfigureAwait(false);
|
||||
await msg.ModifyAsync(m => m.Content = GetText("playlist_queue_complete")).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -392,7 +400,7 @@ namespace NadekoBot.Modules.Music
|
||||
{
|
||||
try
|
||||
{
|
||||
mp.AddSong(new Song(new Classes.SongInfo
|
||||
mp.AddSong(new Song(new SongInfo
|
||||
{
|
||||
Title = svideo.FullName,
|
||||
Provider = "SoundCloud",
|
||||
@ -415,40 +423,39 @@ namespace NadekoBot.Modules.Music
|
||||
var arg = directory;
|
||||
if (string.IsNullOrWhiteSpace(arg))
|
||||
return;
|
||||
try
|
||||
var dir = new DirectoryInfo(arg);
|
||||
var fileEnum = dir.GetFiles("*", SearchOption.AllDirectories)
|
||||
.Where(x => !x.Attributes.HasFlag(FileAttributes.Hidden | FileAttributes.System));
|
||||
var gusr = (IGuildUser)Context.User;
|
||||
foreach (var file in fileEnum)
|
||||
{
|
||||
var dir = new DirectoryInfo(arg);
|
||||
var fileEnum = dir.GetFiles("*", SearchOption.AllDirectories)
|
||||
.Where(x => !x.Attributes.HasFlag(FileAttributes.Hidden | FileAttributes.System));
|
||||
var gusr = (IGuildUser)Context.User;
|
||||
foreach (var file in fileEnum)
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
await QueueSong(((IGuildUser)Context.User), (ITextChannel)Context.Channel, ((IGuildUser)Context.User).VoiceChannel, file.FullName, true, MusicType.Local).ConfigureAwait(false);
|
||||
}
|
||||
catch (PlaylistFullException)
|
||||
{
|
||||
break;
|
||||
}
|
||||
catch { }
|
||||
await QueueSong(gusr, (ITextChannel)Context.Channel, gusr.VoiceChannel, file.FullName, true, MusicType.Local).ConfigureAwait(false);
|
||||
}
|
||||
catch (PlaylistFullException)
|
||||
{
|
||||
break;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
await Context.Channel.SendConfirmAsync("🎵 Directory queue complete.").ConfigureAwait(false);
|
||||
}
|
||||
catch { }
|
||||
await ReplyConfirmLocalized("dir_queue_complete").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task Radio(string radio_link)
|
||||
public async Task Radio(string radioLink)
|
||||
{
|
||||
|
||||
if (((IGuildUser)Context.User).VoiceChannel?.Guild != Context.Guild)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("💢 You need to be in a voice channel on this server.\n If you are already in a voice (ITextChannel)Context.Channel, try rejoining it.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("must_be_in_voice").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
await QueueSong(((IGuildUser)Context.User), (ITextChannel)Context.Channel, ((IGuildUser)Context.User).VoiceChannel, radio_link, musicType: MusicType.Radio).ConfigureAwait(false);
|
||||
await QueueSong(((IGuildUser)Context.User), (ITextChannel)Context.Channel, ((IGuildUser)Context.User).VoiceChannel, radioLink, musicType: MusicType.Radio).ConfigureAwait(false);
|
||||
if ((await Context.Guild.GetCurrentUserAsync()).GetPermissions((IGuildChannel)Context.Channel).ManageMessages)
|
||||
{
|
||||
Context.Message.DeleteAfter(10);
|
||||
@ -505,20 +512,20 @@ namespace NadekoBot.Modules.Music
|
||||
MusicPlayer musicPlayer;
|
||||
if (!MusicPlayers.TryGetValue(Context.Guild.Id, out musicPlayer)) return;
|
||||
musicPlayer.ClearQueue();
|
||||
await Context.Channel.SendConfirmAsync($"🎵 Queue cleared!").ConfigureAwait(false);
|
||||
return;
|
||||
await ReplyConfirmLocalized("queue_cleared").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task MoveSong([Remainder] string fromto)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(fromto))
|
||||
return;
|
||||
|
||||
MusicPlayer musicPlayer;
|
||||
if (!MusicPlayers.TryGetValue(Context.Guild.Id, out musicPlayer))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
fromto = fromto?.Trim();
|
||||
var fromtoArr = fromto.Split('>');
|
||||
|
||||
@ -531,7 +538,7 @@ namespace NadekoBot.Modules.Music
|
||||
!int.TryParse(fromtoArr[1], out n2) || n1 < 1 || n2 < 1 || n1 == n2 ||
|
||||
n1 > playlist.Count || n2 > playlist.Count)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("Invalid input.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("invalid_input").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -542,10 +549,10 @@ namespace NadekoBot.Modules.Music
|
||||
|
||||
var embed = new EmbedBuilder()
|
||||
.WithTitle($"{s.SongInfo.Title.TrimTo(70)}")
|
||||
.WithUrl($"{s.SongInfo.Query}")
|
||||
.WithAuthor(eab => eab.WithName("Song Moved").WithIconUrl("https://cdn.discordapp.com/attachments/155726317222887425/258605269972549642/music1.png"))
|
||||
.AddField(fb => fb.WithName("**From Position**").WithValue($"#{n1}").WithIsInline(true))
|
||||
.AddField(fb => fb.WithName("**To Position**").WithValue($"#{n2}").WithIsInline(true))
|
||||
.WithUrl(s.SongUrl)
|
||||
.WithAuthor(eab => eab.WithName(GetText("song_moved")).WithIconUrl("https://cdn.discordapp.com/attachments/155726317222887425/258605269972549642/music1.png"))
|
||||
.AddField(fb => fb.WithName(GetText("from_position")).WithValue($"#{n1}").WithIsInline(true))
|
||||
.AddField(fb => fb.WithName(GetText("to_position")).WithValue($"#{n2}").WithIsInline(true))
|
||||
.WithColor(NadekoBot.OkColor);
|
||||
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
|
||||
|
||||
@ -556,14 +563,18 @@ namespace NadekoBot.Modules.Music
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task SetMaxQueue(uint size)
|
||||
public async Task SetMaxQueue(uint size = 0)
|
||||
{
|
||||
MusicPlayer musicPlayer;
|
||||
if (!MusicPlayers.TryGetValue(Context.Guild.Id, out musicPlayer))
|
||||
return;
|
||||
|
||||
musicPlayer.MaxQueueSize = size;
|
||||
await Context.Channel.SendConfirmAsync($"🎵 Max queue set to {(size == 0 ? ("unlimited") : size + " tracks")}.");
|
||||
|
||||
if(size == 0)
|
||||
await ReplyConfirmLocalized("max_queue_unlimited").ConfigureAwait(false);
|
||||
else
|
||||
await ReplyConfirmLocalized("max_queue_x", size).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -579,9 +590,9 @@ namespace NadekoBot.Modules.Music
|
||||
return;
|
||||
musicPlayer.MaxPlaytimeSeconds = seconds;
|
||||
if (seconds == 0)
|
||||
await channel.SendConfirmAsync($"🎵 Max playtime has no limit now.");
|
||||
await ReplyConfirmLocalized("max_playtime_none").ConfigureAwait(false);
|
||||
else
|
||||
await channel.SendConfirmAsync($"🎵 Max playtime set to {seconds} seconds.");
|
||||
await ReplyConfirmLocalized("max_playtime_set", seconds).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -600,11 +611,11 @@ namespace NadekoBot.Modules.Music
|
||||
if (currentValue)
|
||||
await Context.Channel.EmbedAsync(new EmbedBuilder()
|
||||
.WithOkColor()
|
||||
.WithAuthor(eab => eab.WithMusicIcon().WithName("🔂 Repeating track"))
|
||||
.WithAuthor(eab => eab.WithMusicIcon().WithName("🔂 " + GetText("repeating_track")))
|
||||
.WithDescription(currentSong.PrettyName)
|
||||
.WithFooter(ef => ef.WithText(currentSong.PrettyInfo))).ConfigureAwait(false);
|
||||
else
|
||||
await Context.Channel.SendConfirmAsync($"🔂 Current track repeat stopped.")
|
||||
await Context.Channel.SendConfirmAsync("🔂 " + GetText("repeating_track_stopped"))
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@ -617,7 +628,10 @@ namespace NadekoBot.Modules.Music
|
||||
if (!MusicPlayers.TryGetValue(Context.Guild.Id, out musicPlayer))
|
||||
return;
|
||||
var currentValue = musicPlayer.ToggleRepeatPlaylist();
|
||||
await Context.Channel.SendConfirmAsync($"🔁 Repeat playlist {(currentValue ? "**enabled**." : "**disabled**.")}").ConfigureAwait(false);
|
||||
if(currentValue)
|
||||
await ReplyConfirmLocalized("rpl_enabled").ConfigureAwait(false);
|
||||
else
|
||||
await ReplyConfirmLocalized("rpl_disabled").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -654,7 +668,10 @@ namespace NadekoBot.Modules.Music
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await Context.Channel.SendConfirmAsync(($"🎵 Saved playlist as **{name}**, ID: {playlist.Id}.")).ConfigureAwait(false);
|
||||
await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||
.WithTitle(GetText("playlist_saved"))
|
||||
.AddField(efb => efb.WithName(GetText("name")).WithValue(name))
|
||||
.AddField(efb => efb.WithName(GetText("id")).WithValue(playlist.Id.ToString())));
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -669,11 +686,11 @@ namespace NadekoBot.Modules.Music
|
||||
|
||||
if (mpl == null)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("Can't find playlist with that ID.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("playlist_id_not_found").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
IUserMessage msg = null;
|
||||
try { msg = await Context.Channel.SendMessageAsync($"🎶 Attempting to load **{mpl.Songs.Count}** songs...").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
|
||||
try { msg = await Context.Channel.SendMessageAsync(GetText("attempting_to_queue", Format.Bold(mpl.Songs.Count.ToString()))).ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
|
||||
foreach (var item in mpl.Songs)
|
||||
{
|
||||
var usr = (IGuildUser)Context.User;
|
||||
@ -685,15 +702,13 @@ namespace NadekoBot.Modules.Music
|
||||
catch { break; }
|
||||
}
|
||||
if (msg != null)
|
||||
await msg.ModifyAsync(m => m.Content = $"✅ Done loading playlist **{mpl.Name}**.").ConfigureAwait(false);
|
||||
await msg.ModifyAsync(m => m.Content = GetText("playlist_queue_complete")).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task Playlists([Remainder] int num = 1)
|
||||
{
|
||||
|
||||
|
||||
if (num <= 0)
|
||||
return;
|
||||
|
||||
@ -705,8 +720,9 @@ namespace NadekoBot.Modules.Music
|
||||
}
|
||||
|
||||
var embed = new EmbedBuilder()
|
||||
.WithAuthor(eab => eab.WithName($"Page {num} of Saved Playlists").WithMusicIcon())
|
||||
.WithDescription(string.Join("\n", playlists.Select(r => $"`#{r.Id}` - **{r.Name}** by *{r.Author}* ({r.Songs.Count} songs)")))
|
||||
.WithAuthor(eab => eab.WithName(GetText("playlists_page", num)).WithMusicIcon())
|
||||
.WithDescription(string.Join("\n", playlists.Select(r =>
|
||||
GetText("playlists", "#" + r.Id, r.Name, r.Author, r.Songs.Count))))
|
||||
.WithOkColor();
|
||||
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
|
||||
|
||||
@ -716,13 +732,12 @@ namespace NadekoBot.Modules.Music
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task DeletePlaylist([Remainder] int id)
|
||||
{
|
||||
bool success = false;
|
||||
MusicPlaylist pl = null;
|
||||
var success = false;
|
||||
try
|
||||
{
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
{
|
||||
pl = uow.MusicPlaylists.Get(id);
|
||||
var pl = uow.MusicPlaylists.Get(id);
|
||||
|
||||
if (pl != null)
|
||||
{
|
||||
@ -732,15 +747,13 @@ namespace NadekoBot.Modules.Music
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
success = true;
|
||||
}
|
||||
else
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!success)
|
||||
await Context.Channel.SendErrorAsync("Failed to delete that playlist. It either doesn't exist, or you are not its author.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("playlist_delete_fail").ConfigureAwait(false);
|
||||
else
|
||||
await Context.Channel.SendConfirmAsync("🗑 Playlist successfully **deleted**.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("playlist_deleted").ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -780,7 +793,7 @@ namespace NadekoBot.Modules.Music
|
||||
if (seconds.Length == 1)
|
||||
seconds = "0" + seconds;
|
||||
|
||||
await Context.Channel.SendConfirmAsync($"Skipped to `{minutes}:{seconds}`").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("skipped_to", minutes, seconds).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -792,9 +805,9 @@ namespace NadekoBot.Modules.Music
|
||||
return;
|
||||
|
||||
if (!musicPlayer.ToggleAutoplay())
|
||||
await Context.Channel.SendConfirmAsync("❌ Autoplay disabled.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("autoplay_disabled").ConfigureAwait(false);
|
||||
else
|
||||
await Context.Channel.SendConfirmAsync("✅ Autoplay enabled.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("autoplay_enabled").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -805,29 +818,29 @@ namespace NadekoBot.Modules.Music
|
||||
MusicPlayer musicPlayer;
|
||||
if (!MusicPlayers.TryGetValue(Context.Guild.Id, out musicPlayer))
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("Music must be playing before you set an ouput channel.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("no_player").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
musicPlayer.OutputTextChannel = (ITextChannel)Context.Channel;
|
||||
|
||||
await Context.Channel.SendConfirmAsync("I will now output playing, finished, paused and removed songs in this channel.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("set_music_channel").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public static async Task QueueSong(IGuildUser queuer, ITextChannel textCh, IVoiceChannel voiceCh, string query, bool silent = false, MusicType musicType = MusicType.Normal)
|
||||
public async Task QueueSong(IGuildUser queuer, ITextChannel textCh, IVoiceChannel voiceCh, string query, bool silent = false, MusicType musicType = MusicType.Normal)
|
||||
{
|
||||
if (voiceCh == null || voiceCh.Guild != textCh.Guild)
|
||||
{
|
||||
if (!silent)
|
||||
await textCh.SendErrorAsync($"💢 You need to be in a voice channel on this server.").ConfigureAwait(false);
|
||||
await textCh.SendErrorAsync(GetText("must_be_in_voice")).ConfigureAwait(false);
|
||||
throw new ArgumentNullException(nameof(voiceCh));
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(query) || query.Length < 3)
|
||||
throw new ArgumentException("💢 Invalid query for queue song.", nameof(query));
|
||||
throw new ArgumentException("Invalid song query.", nameof(query));
|
||||
|
||||
var musicPlayer = MusicPlayers.GetOrAdd(textCh.Guild.Id, server =>
|
||||
{
|
||||
float vol = 1;// SpecificConfigurations.Default.Of(server.Id).DefaultMusicVolume;
|
||||
float vol;// SpecificConfigurations.Default.Of(server.Id).DefaultMusicVolume;
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
{
|
||||
vol = uow.GuildConfigs.For(textCh.Guild.Id, set => set).DefaultMusicVolume;
|
||||
@ -839,14 +852,20 @@ namespace NadekoBot.Modules.Music
|
||||
{
|
||||
try
|
||||
{
|
||||
if (lastFinishedMessage != null)
|
||||
lastFinishedMessage.DeleteAfter(0);
|
||||
lastFinishedMessage?.DeleteAfter(0);
|
||||
|
||||
lastFinishedMessage = await mp.OutputTextChannel.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||
.WithAuthor(eab => eab.WithName("Finished Song").WithMusicIcon())
|
||||
.WithDescription(song.PrettyName)
|
||||
.WithFooter(ef => ef.WithText(song.PrettyInfo)))
|
||||
.ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
lastFinishedMessage = await mp.OutputTextChannel.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||
.WithAuthor(eab => eab.WithName(GetText("finished_song")).WithMusicIcon())
|
||||
.WithDescription(song.PrettyName)
|
||||
.WithFooter(ef => ef.WithText(song.PrettyInfo)))
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
if (mp.Autoplay && mp.Playlist.Count == 0 && song.SongInfo.ProviderType == MusicType.Normal)
|
||||
{
|
||||
@ -856,31 +875,39 @@ namespace NadekoBot.Modules.Music
|
||||
textCh,
|
||||
voiceCh,
|
||||
relatedVideos[new NadekoRandom().Next(0, relatedVideos.Count)],
|
||||
silent,
|
||||
musicType).ConfigureAwait(false);
|
||||
true).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
};
|
||||
|
||||
mp.OnStarted += async (player, song) =>
|
||||
{
|
||||
try { await mp.UpdateSongDurationsAsync().ConfigureAwait(false); } catch { }
|
||||
var sender = player as MusicPlayer;
|
||||
try { await mp.UpdateSongDurationsAsync().ConfigureAwait(false); }
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
var sender = player;
|
||||
if (sender == null)
|
||||
return;
|
||||
try
|
||||
{
|
||||
if (playingMessage != null)
|
||||
playingMessage.DeleteAfter(0);
|
||||
playingMessage?.DeleteAfter(0);
|
||||
|
||||
playingMessage = await mp.OutputTextChannel.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||
.WithAuthor(eab => eab.WithName("Playing Song").WithMusicIcon())
|
||||
.WithAuthor(eab => eab.WithName(GetText("playing_song")).WithMusicIcon())
|
||||
.WithDescription(song.PrettyName)
|
||||
.WithFooter(ef => ef.WithText(song.PrettyInfo)))
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
catch { }
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
};
|
||||
mp.OnPauseChanged += async (paused) =>
|
||||
{
|
||||
@ -888,14 +915,16 @@ namespace NadekoBot.Modules.Music
|
||||
{
|
||||
IUserMessage msg;
|
||||
if (paused)
|
||||
msg = await mp.OutputTextChannel.SendConfirmAsync("🎵 Music playback **paused**.").ConfigureAwait(false);
|
||||
msg = await mp.OutputTextChannel.SendConfirmAsync(GetText("paused")).ConfigureAwait(false);
|
||||
else
|
||||
msg = await mp.OutputTextChannel.SendConfirmAsync("🎵 Music playback **resumed**.").ConfigureAwait(false);
|
||||
msg = await mp.OutputTextChannel.SendConfirmAsync(GetText("resumed")).ConfigureAwait(false);
|
||||
|
||||
if (msg != null)
|
||||
msg.DeleteAfter(10);
|
||||
msg?.DeleteAfter(10);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
catch { }
|
||||
};
|
||||
|
||||
mp.SongRemoved += async (song, index) =>
|
||||
@ -903,7 +932,7 @@ namespace NadekoBot.Modules.Music
|
||||
try
|
||||
{
|
||||
var embed = new EmbedBuilder()
|
||||
.WithAuthor(eab => eab.WithName("Removed song #" + (index + 1)).WithMusicIcon())
|
||||
.WithAuthor(eab => eab.WithName(GetText("removed_song") + " #" + (index + 1)).WithMusicIcon())
|
||||
.WithDescription(song.PrettyName)
|
||||
.WithFooter(ef => ef.WithText(song.PrettyInfo))
|
||||
.WithErrorColor();
|
||||
@ -911,7 +940,10 @@ namespace NadekoBot.Modules.Music
|
||||
await mp.OutputTextChannel.EmbedAsync(embed).ConfigureAwait(false);
|
||||
|
||||
}
|
||||
catch { }
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
};
|
||||
return mp;
|
||||
});
|
||||
@ -928,7 +960,14 @@ namespace NadekoBot.Modules.Music
|
||||
}
|
||||
catch (PlaylistFullException)
|
||||
{
|
||||
try { await textCh.SendConfirmAsync($"🎵 Queue is full at **{musicPlayer.MaxQueueSize}/{musicPlayer.MaxQueueSize}**."); } catch { }
|
||||
try
|
||||
{
|
||||
await textCh.SendConfirmAsync(GetText("queue_full", musicPlayer.MaxQueueSize));
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
throw;
|
||||
}
|
||||
if (!silent)
|
||||
@ -937,15 +976,17 @@ namespace NadekoBot.Modules.Music
|
||||
{
|
||||
//var queuedMessage = await textCh.SendConfirmAsync($"🎵 Queued **{resolvedSong.SongInfo.Title}** at `#{musicPlayer.Playlist.Count + 1}`").ConfigureAwait(false);
|
||||
var queuedMessage = await textCh.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||
.WithAuthor(eab => eab.WithName("Queued Song #" + (musicPlayer.Playlist.Count + 1)).WithMusicIcon())
|
||||
.WithDescription($"{resolvedSong.PrettyName}\nQueue ")
|
||||
.WithAuthor(eab => eab.WithName(GetText("queued_song") + " #" + (musicPlayer.Playlist.Count + 1)).WithMusicIcon())
|
||||
.WithDescription($"{resolvedSong.PrettyName}\n{GetText("queue")} ")
|
||||
.WithThumbnailUrl(resolvedSong.Thumbnail)
|
||||
.WithFooter(ef => ef.WithText(resolvedSong.PrettyProvider)))
|
||||
.ConfigureAwait(false);
|
||||
if (queuedMessage != null)
|
||||
queuedMessage.DeleteAfter(10);
|
||||
queuedMessage?.DeleteAfter(10);
|
||||
}
|
||||
catch { } // if queued message sending fails, don't attempt to delete it
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
} // if queued message sending fails, don't attempt to delete it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,8 +7,6 @@ using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using NadekoBot.Services;
|
||||
using System.Net.Http;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Xml.Linq;
|
||||
using NadekoBot.Extensions;
|
||||
using System.Xml;
|
||||
using System.Threading;
|
||||
@ -17,11 +15,11 @@ using System.Collections.Concurrent;
|
||||
namespace NadekoBot.Modules.NSFW
|
||||
{
|
||||
[NadekoModule("NSFW", "~")]
|
||||
public class NSFW : DiscordModule
|
||||
public class NSFW : NadekoTopLevelModule
|
||||
{
|
||||
#if !GLOBAL_NADEKO
|
||||
private static ConcurrentDictionary<ulong, Timer> AutoHentaiTimers { get; } = new ConcurrentDictionary<ulong, Timer>();
|
||||
private static ConcurrentHashSet<ulong> _hentaiBombBlacklist { get; } = new ConcurrentHashSet<ulong>();
|
||||
|
||||
private static readonly ConcurrentDictionary<ulong, Timer> _autoHentaiTimers = new ConcurrentDictionary<ulong, Timer>();
|
||||
private static readonly ConcurrentHashSet<ulong> _hentaiBombBlacklist = new ConcurrentHashSet<ulong>();
|
||||
|
||||
private async Task InternalHentai(IMessageChannel channel, string tag, bool noError)
|
||||
{
|
||||
@ -30,7 +28,7 @@ namespace NadekoBot.Modules.NSFW
|
||||
tag = "rating%3Aexplicit+" + tag;
|
||||
|
||||
var rng = new NadekoRandom();
|
||||
Task<string> provider = Task.FromResult("");
|
||||
var provider = Task.FromResult("");
|
||||
switch (rng.Next(0, 4))
|
||||
{
|
||||
case 0:
|
||||
@ -45,27 +43,25 @@ namespace NadekoBot.Modules.NSFW
|
||||
case 3:
|
||||
provider = GetYandereImageLink(tag);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
var link = await provider.ConfigureAwait(false);
|
||||
if (string.IsNullOrWhiteSpace(link))
|
||||
{
|
||||
if (!noError)
|
||||
await channel.SendErrorAsync("No results found.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("not_found").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
await channel.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||
.WithImageUrl(link)
|
||||
.WithDescription("Tag: " + tag))
|
||||
.WithDescription($"{GetText("tag")}: " + tag))
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public Task Hentai([Remainder] string tag = null) =>
|
||||
InternalHentai(Context.Channel, tag, false);
|
||||
|
||||
#if !GLOBAL_NADEKO
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireUserPermission(ChannelPermission.ManageMessages)]
|
||||
public async Task AutoHentai(int interval = 0, string tags = null)
|
||||
@ -74,11 +70,10 @@ namespace NadekoBot.Modules.NSFW
|
||||
|
||||
if (interval == 0)
|
||||
{
|
||||
if (AutoHentaiTimers.TryRemove(Context.Channel.Id, out t))
|
||||
{
|
||||
t.Change(Timeout.Infinite, Timeout.Infinite); //proper way to disable the timer
|
||||
await Context.Channel.SendConfirmAsync("Autohentai stopped.").ConfigureAwait(false);
|
||||
}
|
||||
if (!_autoHentaiTimers.TryRemove(Context.Channel.Id, out t)) return;
|
||||
|
||||
t.Change(Timeout.Infinite, Timeout.Infinite); //proper way to disable the timer
|
||||
await ReplyConfirmLocalized("autohentai_stopped").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -96,17 +91,21 @@ namespace NadekoBot.Modules.NSFW
|
||||
else
|
||||
await InternalHentai(Context.Channel, tagsArr[new NadekoRandom().Next(0, tagsArr.Length)], true).ConfigureAwait(false);
|
||||
}
|
||||
catch { }
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}, null, interval * 1000, interval * 1000);
|
||||
|
||||
AutoHentaiTimers.AddOrUpdate(Context.Channel.Id, t, (key, old) =>
|
||||
_autoHentaiTimers.AddOrUpdate(Context.Channel.Id, t, (key, old) =>
|
||||
{
|
||||
old.Change(Timeout.Infinite, Timeout.Infinite);
|
||||
return t;
|
||||
});
|
||||
|
||||
await Context.Channel.SendConfirmAsync($"Autohentai started. Reposting every {interval}s with one of the following tags:\n{string.Join(", ", tagsArr)}")
|
||||
.ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("autohentai_started",
|
||||
interval,
|
||||
string.Join(", ", tagsArr)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
|
||||
@ -125,54 +124,29 @@ namespace NadekoBot.Modules.NSFW
|
||||
GetKonachanImageLink(tag),
|
||||
GetYandereImageLink(tag)).ConfigureAwait(false);
|
||||
|
||||
var linksEnum = links?.Where(l => l != null);
|
||||
var linksEnum = links?.Where(l => l != null).ToArray();
|
||||
if (links == null || !linksEnum.Any())
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("No results found.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("not_found").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
await Context.Channel.SendMessageAsync(String.Join("\n\n", linksEnum)).ConfigureAwait(false);
|
||||
await Context.Channel.SendMessageAsync(string.Join("\n\n", linksEnum)).ConfigureAwait(false);
|
||||
}
|
||||
finally {
|
||||
finally
|
||||
{
|
||||
await Task.Delay(5000).ConfigureAwait(false);
|
||||
_hentaiBombBlacklist.TryRemove(Context.User.Id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task Danbooru([Remainder] string tag = null)
|
||||
{
|
||||
tag = tag?.Trim() ?? "";
|
||||
|
||||
var url = await GetDanbooruImageLink(tag).ConfigureAwait(false);
|
||||
|
||||
if (url == null)
|
||||
await Context.Channel.SendErrorAsync(Context.User.Mention + " No results.").ConfigureAwait(false);
|
||||
else
|
||||
await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||
.WithDescription(Context.User.Mention + " " + tag)
|
||||
.WithImageUrl(url)
|
||||
.WithFooter(efb => efb.WithText("Danbooru")))
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
#endif
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public Task Yandere([Remainder] string tag = null)
|
||||
=> Searches.Searches.InternalDapiCommand(Context.Message, tag, Searches.Searches.DapiSearchType.Yandere);
|
||||
=> InternalDapiCommand(Context.Message, tag, Searches.Searches.DapiSearchType.Yandere);
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public Task Konachan([Remainder] string tag = null)
|
||||
=> Searches.Searches.InternalDapiCommand(Context.Message, tag, Searches.Searches.DapiSearchType.Konachan);
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public Task Gelbooru([Remainder] string tag = null)
|
||||
=> Searches.Searches.InternalDapiCommand(Context.Message, tag, Searches.Searches.DapiSearchType.Gelbooru);
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public Task Rule34([Remainder] string tag = null)
|
||||
=> Searches.Searches.InternalDapiCommand(Context.Message, tag, Searches.Searches.DapiSearchType.Rule34);
|
||||
=> InternalDapiCommand(Context.Message, tag, Searches.Searches.DapiSearchType.Konachan);
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task E621([Remainder] string tag = null)
|
||||
@ -182,7 +156,7 @@ namespace NadekoBot.Modules.NSFW
|
||||
var url = await GetE621ImageLink(tag).ConfigureAwait(false);
|
||||
|
||||
if (url == null)
|
||||
await Context.Channel.SendErrorAsync(Context.User.Mention + " No results.");
|
||||
await ReplyErrorLocalized("not_found").ConfigureAwait(false);
|
||||
else
|
||||
await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||
.WithDescription(Context.User.Mention + " " + tag)
|
||||
@ -190,49 +164,28 @@ namespace NadekoBot.Modules.NSFW
|
||||
.WithFooter(efb => efb.WithText("e621")))
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
#endif
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task Cp()
|
||||
{
|
||||
await Context.Channel.SendMessageAsync("http://i.imgur.com/MZkY1md.jpg").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task Boobs()
|
||||
{
|
||||
try
|
||||
{
|
||||
JToken obj;
|
||||
using (var http = new HttpClient())
|
||||
{
|
||||
obj = JArray.Parse(await http.GetStringAsync($"http://api.oboobs.ru/boobs/{ new NadekoRandom().Next(0, 10330) }").ConfigureAwait(false))[0];
|
||||
}
|
||||
await Context.Channel.SendMessageAsync($"http://media.oboobs.ru/{ obj["preview"].ToString() }").ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync(ex.Message).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
public Task Rule34([Remainder] string tag = null)
|
||||
=> InternalDapiCommand(Context.Message, tag, Searches.Searches.DapiSearchType.Rule34);
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task Butts()
|
||||
public async Task Danbooru([Remainder] string tag = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
JToken obj;
|
||||
using (var http = new HttpClient())
|
||||
{
|
||||
obj = JArray.Parse(await http.GetStringAsync($"http://api.obutts.ru/butts/{ new NadekoRandom().Next(0, 4335) }").ConfigureAwait(false))[0];
|
||||
}
|
||||
await Context.Channel.SendMessageAsync($"http://media.obutts.ru/{ obj["preview"].ToString() }").ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync(ex.Message).ConfigureAwait(false);
|
||||
}
|
||||
tag = tag?.Trim() ?? "";
|
||||
|
||||
var url = await GetDanbooruImageLink(tag).ConfigureAwait(false);
|
||||
|
||||
if (url == null)
|
||||
await ReplyErrorLocalized("not_found").ConfigureAwait(false);
|
||||
else
|
||||
await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||
.WithDescription(Context.User.Mention + " " + tag)
|
||||
.WithImageUrl(url)
|
||||
.WithFooter(efb => efb.WithText("Danbooru")))
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
#if !GLOBAL_NADEKO
|
||||
|
||||
public static Task<string> GetDanbooruImageLink(string tag) => Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
@ -255,6 +208,51 @@ namespace NadekoBot.Modules.NSFW
|
||||
}
|
||||
});
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public Task Gelbooru([Remainder] string tag = null)
|
||||
=> InternalDapiCommand(Context.Message, tag, Searches.Searches.DapiSearchType.Gelbooru);
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task Cp()
|
||||
{
|
||||
await Context.Channel.SendMessageAsync("http://i.imgur.com/MZkY1md.jpg").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task Boobs()
|
||||
{
|
||||
try
|
||||
{
|
||||
JToken obj;
|
||||
using (var http = new HttpClient())
|
||||
{
|
||||
obj = JArray.Parse(await http.GetStringAsync($"http://api.oboobs.ru/boobs/{new NadekoRandom().Next(0, 10330)}").ConfigureAwait(false))[0];
|
||||
}
|
||||
await Context.Channel.SendMessageAsync($"http://media.oboobs.ru/{obj["preview"]}").ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync(ex.Message).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task Butts()
|
||||
{
|
||||
try
|
||||
{
|
||||
JToken obj;
|
||||
using (var http = new HttpClient())
|
||||
{
|
||||
obj = JArray.Parse(await http.GetStringAsync($"http://api.obutts.ru/butts/{new NadekoRandom().Next(0, 4335)}").ConfigureAwait(false))[0];
|
||||
}
|
||||
await Context.Channel.SendMessageAsync($"http://media.obutts.ru/{obj["preview"]}").ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync(ex.Message).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
public static Task<string> GetE621ImageLink(string tag) => Task.Run(async () =>
|
||||
{
|
||||
@ -278,6 +276,9 @@ namespace NadekoBot.Modules.NSFW
|
||||
}
|
||||
});
|
||||
|
||||
public static Task<string> GetRule34ImageLink(string tag) =>
|
||||
Searches.Searches.InternalDapiSearch(tag, Searches.Searches.DapiSearchType.Rule34);
|
||||
|
||||
public static Task<string> GetYandereImageLink(string tag) =>
|
||||
Searches.Searches.InternalDapiSearch(tag, Searches.Searches.DapiSearchType.Yandere);
|
||||
|
||||
@ -287,8 +288,21 @@ namespace NadekoBot.Modules.NSFW
|
||||
public static Task<string> GetGelbooruImageLink(string tag) =>
|
||||
Searches.Searches.InternalDapiSearch(tag, Searches.Searches.DapiSearchType.Gelbooru);
|
||||
|
||||
public static Task<string> GetRule34ImageLink(string tag) =>
|
||||
Searches.Searches.InternalDapiSearch(tag, Searches.Searches.DapiSearchType.Rule34);
|
||||
#endif
|
||||
public async Task InternalDapiCommand(IUserMessage umsg, string tag, Searches.Searches.DapiSearchType type)
|
||||
{
|
||||
var channel = umsg.Channel;
|
||||
|
||||
tag = tag?.Trim() ?? "";
|
||||
|
||||
var url = await Searches.Searches.InternalDapiSearch(tag, type).ConfigureAwait(false);
|
||||
|
||||
if (url == null)
|
||||
await channel.SendErrorAsync(umsg.Author.Mention + " " + GetText("no_results"));
|
||||
else
|
||||
await channel.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||
.WithDescription(umsg.Author.Mention + " " + tag)
|
||||
.WithImageUrl(url)
|
||||
.WithFooter(efb => efb.WithText(type.ToString()))).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
129
src/NadekoBot/Modules/NadekoModule.cs
Normal file
129
src/NadekoBot/Modules/NadekoModule.cs
Normal file
@ -0,0 +1,129 @@
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using NadekoBot.Extensions;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Globalization;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NadekoBot.Modules
|
||||
{
|
||||
public abstract class NadekoTopLevelModule : ModuleBase
|
||||
{
|
||||
protected readonly Logger _log;
|
||||
protected CultureInfo _cultureInfo;
|
||||
public readonly string Prefix;
|
||||
public readonly string ModuleTypeName;
|
||||
public readonly string LowerModuleTypeName;
|
||||
|
||||
protected NadekoTopLevelModule(bool isTopLevelModule = true)
|
||||
{
|
||||
//if it's top level module
|
||||
ModuleTypeName = isTopLevelModule ? this.GetType().Name : this.GetType().DeclaringType.Name;
|
||||
LowerModuleTypeName = ModuleTypeName.ToLowerInvariant();
|
||||
|
||||
if (!NadekoBot.ModulePrefixes.TryGetValue(ModuleTypeName, out Prefix))
|
||||
Prefix = "?err?";
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
}
|
||||
|
||||
protected override void BeforeExecute()
|
||||
{
|
||||
_cultureInfo = NadekoBot.Localization.GetCultureInfo(Context.Guild?.Id);
|
||||
|
||||
_log.Info("Culture info is {0}", _cultureInfo);
|
||||
}
|
||||
|
||||
//public Task<IUserMessage> ReplyConfirmLocalized(string titleKey, string textKey, string url = null, string footer = null)
|
||||
//{
|
||||
// var title = NadekoBot.ResponsesResourceManager.GetString(titleKey, cultureInfo);
|
||||
// var text = NadekoBot.ResponsesResourceManager.GetString(textKey, cultureInfo);
|
||||
// return Context.Channel.SendConfirmAsync(title, text, url, footer);
|
||||
//}
|
||||
|
||||
//public Task<IUserMessage> ReplyConfirmLocalized(string textKey)
|
||||
//{
|
||||
// var text = NadekoBot.ResponsesResourceManager.GetString(textKey, cultureInfo);
|
||||
// return Context.Channel.SendConfirmAsync(Context.User.Mention + " " + textKey);
|
||||
//}
|
||||
|
||||
//public Task<IUserMessage> ReplyErrorLocalized(string titleKey, string textKey, string url = null, string footer = null)
|
||||
//{
|
||||
// var title = NadekoBot.ResponsesResourceManager.GetString(titleKey, cultureInfo);
|
||||
// var text = NadekoBot.ResponsesResourceManager.GetString(textKey, cultureInfo);
|
||||
// return Context.Channel.SendErrorAsync(title, text, url, footer);
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// Used as failsafe in case response key doesn't exist in the selected or default language.
|
||||
/// </summary>
|
||||
private static readonly CultureInfo _usCultureInfo = new CultureInfo("en-US");
|
||||
|
||||
public static string GetTextStatic(string key, CultureInfo cultureInfo, string lowerModuleTypeName)
|
||||
{
|
||||
var text = NadekoBot.ResponsesResourceManager.GetString(lowerModuleTypeName + "_" + key, cultureInfo);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
{
|
||||
LogManager.GetCurrentClassLogger().Warn(lowerModuleTypeName + "_" + key + " key is missing from " + cultureInfo + " response strings. PLEASE REPORT THIS.");
|
||||
text = NadekoBot.ResponsesResourceManager.GetString(lowerModuleTypeName + "_" + key, _usCultureInfo) ?? $"Error: dkey {lowerModuleTypeName + "_" + key} not found!";
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
return "I can't tell you is the command executed, because there was an error printing out the response. Key '" +
|
||||
lowerModuleTypeName + "_" + key + "' " + "is missing from resources. Please report this.";
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
public static string GetTextStatic(string key, CultureInfo cultureInfo, string lowerModuleTypeName,
|
||||
params object[] replacements)
|
||||
{
|
||||
try
|
||||
{
|
||||
return string.Format(GetTextStatic(key, cultureInfo, lowerModuleTypeName), replacements);
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
return "I cant tell if you command is executed, because there was an error printing out the response. Key '" +
|
||||
lowerModuleTypeName + "_" + key + "' " + "is not properly formatted. Please report this.";
|
||||
}
|
||||
}
|
||||
|
||||
protected string GetText(string key) =>
|
||||
GetTextStatic(key, _cultureInfo, LowerModuleTypeName);
|
||||
|
||||
protected string GetText(string key, params object[] replacements) =>
|
||||
GetTextStatic(key, _cultureInfo, LowerModuleTypeName, replacements);
|
||||
|
||||
public Task<IUserMessage> ErrorLocalized(string textKey, params object[] replacements)
|
||||
{
|
||||
var text = GetText(textKey, replacements);
|
||||
return Context.Channel.SendErrorAsync(text);
|
||||
}
|
||||
|
||||
public Task<IUserMessage> ReplyErrorLocalized(string textKey, params object[] replacements)
|
||||
{
|
||||
var text = GetText(textKey, replacements);
|
||||
return Context.Channel.SendErrorAsync(Context.User.Mention + " " + text);
|
||||
}
|
||||
|
||||
public Task<IUserMessage> ConfirmLocalized(string textKey, params object[] replacements)
|
||||
{
|
||||
var text = GetText(textKey, replacements);
|
||||
return Context.Channel.SendConfirmAsync(text);
|
||||
}
|
||||
|
||||
public Task<IUserMessage> ReplyConfirmLocalized(string textKey, params object[] replacements)
|
||||
{
|
||||
var text = GetText(textKey, replacements);
|
||||
return Context.Channel.SendConfirmAsync(Context.User.Mention + " " + text);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class NadekoSubmodule : NadekoTopLevelModule
|
||||
{
|
||||
protected NadekoSubmodule() : base(false)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
15
src/NadekoBot/Modules/NadekoModuleExtensions.cs
Normal file
15
src/NadekoBot/Modules/NadekoModuleExtensions.cs
Normal file
@ -0,0 +1,15 @@
|
||||
using Discord;
|
||||
using NadekoBot.Extensions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NadekoBot.Modules
|
||||
{
|
||||
public static class NadekoModuleExtensions
|
||||
{
|
||||
|
||||
}
|
||||
}
|
@ -21,11 +21,11 @@ namespace NadekoBot.Modules.Permissions
|
||||
}
|
||||
|
||||
[Group]
|
||||
public class BlacklistCommands : ModuleBase
|
||||
public class BlacklistCommands : NadekoSubmodule
|
||||
{
|
||||
public static ConcurrentHashSet<ulong> BlacklistedUsers { get; set; } = new ConcurrentHashSet<ulong>();
|
||||
public static ConcurrentHashSet<ulong> BlacklistedGuilds { get; set; } = new ConcurrentHashSet<ulong>();
|
||||
public static ConcurrentHashSet<ulong> BlacklistedChannels { get; set; } = new ConcurrentHashSet<ulong>();
|
||||
public static ConcurrentHashSet<ulong> BlacklistedUsers { get; set; }
|
||||
public static ConcurrentHashSet<ulong> BlacklistedGuilds { get; set; }
|
||||
public static ConcurrentHashSet<ulong> BlacklistedChannels { get; set; }
|
||||
|
||||
static BlacklistCommands()
|
||||
{
|
||||
@ -115,7 +115,7 @@ namespace NadekoBot.Modules.Permissions
|
||||
}
|
||||
break;
|
||||
case BlacklistType.Channel:
|
||||
var item = Games.Games.TriviaCommands.RunningTrivias.FirstOrDefault(kvp => kvp.Value.channel.Id == id);
|
||||
var item = Games.Games.TriviaCommands.RunningTrivias.FirstOrDefault(kvp => kvp.Value.Channel.Id == id);
|
||||
Games.Games.TriviaCommands.RunningTrivias.TryRemove(item.Key, out tg);
|
||||
if (tg != null)
|
||||
{
|
||||
@ -124,16 +124,14 @@ namespace NadekoBot.Modules.Permissions
|
||||
break;
|
||||
case BlacklistType.User:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(action == AddRemove.Add)
|
||||
await Context.Channel.SendConfirmAsync($"Blacklisted a `{type}` with id `{id}`").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("blacklisted", Format.Code(type.ToString()), Format.Code(id.ToString())).ConfigureAwait(false);
|
||||
else
|
||||
await Context.Channel.SendConfirmAsync($"Unblacklisted a `{type}` with id `{id}`").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("unblacklisted", Format.Code(type.ToString()), Format.Code(id.ToString())).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,15 +21,15 @@ namespace NadekoBot.Modules.Permissions
|
||||
}
|
||||
|
||||
[Group]
|
||||
public class CmdCdsCommands : ModuleBase
|
||||
public class CmdCdsCommands : NadekoSubmodule
|
||||
{
|
||||
public static ConcurrentDictionary<ulong, ConcurrentHashSet<CommandCooldown>> commandCooldowns { get; }
|
||||
public static ConcurrentDictionary<ulong, ConcurrentHashSet<CommandCooldown>> CommandCooldowns { get; }
|
||||
private static ConcurrentDictionary<ulong, ConcurrentHashSet<ActiveCooldown>> activeCooldowns { get; } = new ConcurrentDictionary<ulong, ConcurrentHashSet<ActiveCooldown>>();
|
||||
|
||||
static CmdCdsCommands()
|
||||
{
|
||||
var configs = NadekoBot.AllGuildConfigs;
|
||||
commandCooldowns = new ConcurrentDictionary<ulong, ConcurrentHashSet<CommandCooldown>>(configs.ToDictionary(k => k.GuildId, v => new ConcurrentHashSet<CommandCooldown>(v.CommandCooldowns)));
|
||||
CommandCooldowns = new ConcurrentDictionary<ulong, ConcurrentHashSet<CommandCooldown>>(configs.ToDictionary(k => k.GuildId, v => new ConcurrentHashSet<CommandCooldown>(v.CommandCooldowns)));
|
||||
}
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
@ -38,14 +38,14 @@ namespace NadekoBot.Modules.Permissions
|
||||
var channel = (ITextChannel)Context.Channel;
|
||||
if (secs < 0 || secs > 3600)
|
||||
{
|
||||
await channel.SendErrorAsync("Invalid second parameter. (Must be a number between 0 and 3600)").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("invalid_second_param_between", 0, 3600).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
{
|
||||
var config = uow.GuildConfigs.For(channel.Guild.Id, set => set.Include(gc => gc.CommandCooldowns));
|
||||
var localSet = commandCooldowns.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet<CommandCooldown>());
|
||||
var localSet = CommandCooldowns.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet<CommandCooldown>());
|
||||
|
||||
config.CommandCooldowns.RemoveWhere(cc => cc.CommandName == command.Aliases.First().ToLowerInvariant());
|
||||
localSet.RemoveWhere(cc => cc.CommandName == command.Aliases.First().ToLowerInvariant());
|
||||
@ -65,13 +65,14 @@ namespace NadekoBot.Modules.Permissions
|
||||
{
|
||||
var activeCds = activeCooldowns.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet<ActiveCooldown>());
|
||||
activeCds.RemoveWhere(ac => ac.Command == command.Aliases.First().ToLowerInvariant());
|
||||
await channel.SendConfirmAsync($"🚮 Command **{command.Aliases.First()}** has no coooldown now and all existing cooldowns have been cleared.")
|
||||
.ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("cmdcd_cleared",
|
||||
Format.Bold(command.Aliases.First())).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await channel.SendConfirmAsync($"✅ Command **{command.Aliases.First()}** now has a **{secs} {"seconds".SnPl(secs)}** cooldown.")
|
||||
.ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("cmdcd_add",
|
||||
Format.Bold(command.Aliases.First()),
|
||||
Format.Bold(secs.ToString())).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -80,19 +81,19 @@ namespace NadekoBot.Modules.Permissions
|
||||
public async Task AllCmdCooldowns()
|
||||
{
|
||||
var channel = (ITextChannel)Context.Channel;
|
||||
var localSet = commandCooldowns.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet<CommandCooldown>());
|
||||
var localSet = CommandCooldowns.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet<CommandCooldown>());
|
||||
|
||||
if (!localSet.Any())
|
||||
await channel.SendConfirmAsync("ℹ️ `No command cooldowns set.`").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("cmdcd_none").ConfigureAwait(false);
|
||||
else
|
||||
await channel.SendTableAsync("", localSet.Select(c => c.CommandName + ": " + c.Seconds + " secs"), s => $"{s,-30}", 2).ConfigureAwait(false);
|
||||
await channel.SendTableAsync("", localSet.Select(c => c.CommandName + ": " + c.Seconds + GetText("sec")), s => $"{s,-30}", 2).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public static bool HasCooldown(CommandInfo cmd, IGuild guild, IUser user)
|
||||
{
|
||||
if (guild == null)
|
||||
return false;
|
||||
var cmdcds = CmdCdsCommands.commandCooldowns.GetOrAdd(guild.Id, new ConcurrentHashSet<CommandCooldown>());
|
||||
var cmdcds = CmdCdsCommands.CommandCooldowns.GetOrAdd(guild.Id, new ConcurrentHashSet<CommandCooldown>());
|
||||
CommandCooldown cdRule;
|
||||
if ((cdRule = cmdcds.FirstOrDefault(cc => cc.CommandName == cmd.Aliases.First().ToLowerInvariant())) != null)
|
||||
{
|
||||
@ -101,23 +102,23 @@ namespace NadekoBot.Modules.Permissions
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
activeCdsForGuild.Add(new ActiveCooldown()
|
||||
{
|
||||
activeCdsForGuild.Add(new ActiveCooldown()
|
||||
UserId = user.Id,
|
||||
Command = cmd.Aliases.First().ToLowerInvariant(),
|
||||
});
|
||||
var _ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
UserId = user.Id,
|
||||
Command = cmd.Aliases.First().ToLowerInvariant(),
|
||||
});
|
||||
var t = Task.Run(async () =>
|
||||
await Task.Delay(cdRule.Seconds * 1000);
|
||||
activeCdsForGuild.RemoveWhere(ac => ac.Command == cmd.Aliases.First().ToLowerInvariant() && ac.UserId == user.Id);
|
||||
}
|
||||
catch
|
||||
{
|
||||
try
|
||||
{
|
||||
await Task.Delay(cdRule.Seconds * 1000);
|
||||
activeCdsForGuild.RemoveWhere(ac => ac.Command == cmd.Aliases.First().ToLowerInvariant() && ac.UserId == user.Id);
|
||||
}
|
||||
catch { }
|
||||
});
|
||||
}
|
||||
// ignored
|
||||
}
|
||||
});
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ namespace NadekoBot.Modules.Permissions
|
||||
public partial class Permissions
|
||||
{
|
||||
[Group]
|
||||
public class CommandCostCommands : ModuleBase
|
||||
public class CommandCostCommands : NadekoSubmodule
|
||||
{
|
||||
private static readonly ConcurrentDictionary<string, int> _commandCosts = new ConcurrentDictionary<string, int>();
|
||||
public static IReadOnlyDictionary<string, int> CommandCosts => _commandCosts;
|
||||
@ -29,29 +29,29 @@ namespace NadekoBot.Modules.Permissions
|
||||
// x => x.Cost));
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task CmdCosts(int page = 1)
|
||||
{
|
||||
var prices = _commandCosts.ToList();
|
||||
//[NadekoCommand, Usage, Description, Aliases]
|
||||
//public async Task CmdCosts(int page = 1)
|
||||
//{
|
||||
// var prices = _commandCosts.ToList();
|
||||
|
||||
if (!prices.Any())
|
||||
{
|
||||
await Context.Channel.SendConfirmAsync("No costs set.").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
// if (!prices.Any())
|
||||
// {
|
||||
// await Context.Channel.SendConfirmAsync(GetText("no_costs")).ConfigureAwait(false);
|
||||
// return;
|
||||
// }
|
||||
|
||||
await Context.Channel.SendPaginatedConfirmAsync(page, (curPage) => {
|
||||
var embed = new EmbedBuilder().WithOkColor()
|
||||
.WithTitle("Command Costs");
|
||||
var current = prices.Skip((curPage - 1) * 9)
|
||||
.Take(9);
|
||||
foreach (var price in current)
|
||||
{
|
||||
embed.AddField(efb => efb.WithName(price.Key).WithValue(price.Value.ToString()).WithIsInline(true));
|
||||
}
|
||||
return embed;
|
||||
}, prices.Count / 9).ConfigureAwait(false);
|
||||
}
|
||||
// await Context.Channel.SendPaginatedConfirmAsync(page, (curPage) => {
|
||||
// var embed = new EmbedBuilder().WithOkColor()
|
||||
// .WithTitle(GetText("command_costs"));
|
||||
// var current = prices.Skip((curPage - 1) * 9)
|
||||
// .Take(9);
|
||||
// foreach (var price in current)
|
||||
// {
|
||||
// embed.AddField(efb => efb.WithName(price.Key).WithValue(price.Value.ToString()).WithIsInline(true));
|
||||
// }
|
||||
// return embed;
|
||||
// }, prices.Count / 9).ConfigureAwait(false);
|
||||
//}
|
||||
|
||||
//[NadekoCommand, Usage, Description, Aliases]
|
||||
//public async Task CommandCost(int cost, CommandInfo cmd)
|
||||
|
@ -13,13 +13,13 @@ namespace NadekoBot.Modules.Permissions
|
||||
public partial class Permissions
|
||||
{
|
||||
[Group]
|
||||
public class FilterCommands : ModuleBase
|
||||
public class FilterCommands : NadekoSubmodule
|
||||
{
|
||||
public static ConcurrentHashSet<ulong> InviteFilteringChannels { get; }
|
||||
public static ConcurrentHashSet<ulong> InviteFilteringServers { get; }
|
||||
|
||||
//serverid, filteredwords
|
||||
private static ConcurrentDictionary<ulong, ConcurrentHashSet<string>> ServerFilteredWords { get; }
|
||||
private static ConcurrentDictionary<ulong, ConcurrentHashSet<string>> serverFilteredWords { get; }
|
||||
|
||||
public static ConcurrentHashSet<ulong> WordFilteringChannels { get; }
|
||||
public static ConcurrentHashSet<ulong> WordFilteringServers { get; }
|
||||
@ -28,7 +28,7 @@ namespace NadekoBot.Modules.Permissions
|
||||
{
|
||||
ConcurrentHashSet<string> words = new ConcurrentHashSet<string>();
|
||||
if(WordFilteringChannels.Contains(channelId))
|
||||
ServerFilteredWords.TryGetValue(guildId, out words);
|
||||
serverFilteredWords.TryGetValue(guildId, out words);
|
||||
return words;
|
||||
}
|
||||
|
||||
@ -36,29 +36,25 @@ namespace NadekoBot.Modules.Permissions
|
||||
{
|
||||
var words = new ConcurrentHashSet<string>();
|
||||
if(WordFilteringServers.Contains(guildId))
|
||||
ServerFilteredWords.TryGetValue(guildId, out words);
|
||||
serverFilteredWords.TryGetValue(guildId, out words);
|
||||
return words;
|
||||
}
|
||||
|
||||
static FilterCommands()
|
||||
{
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
{
|
||||
var guildConfigs = NadekoBot.AllGuildConfigs;
|
||||
var guildConfigs = NadekoBot.AllGuildConfigs;
|
||||
|
||||
InviteFilteringServers = new ConcurrentHashSet<ulong>(guildConfigs.Where(gc => gc.FilterInvites).Select(gc => gc.GuildId));
|
||||
InviteFilteringChannels = new ConcurrentHashSet<ulong>(guildConfigs.SelectMany(gc => gc.FilterInvitesChannelIds.Select(fci => fci.ChannelId)));
|
||||
InviteFilteringServers = new ConcurrentHashSet<ulong>(guildConfigs.Where(gc => gc.FilterInvites).Select(gc => gc.GuildId));
|
||||
InviteFilteringChannels = new ConcurrentHashSet<ulong>(guildConfigs.SelectMany(gc => gc.FilterInvitesChannelIds.Select(fci => fci.ChannelId)));
|
||||
|
||||
var dict = guildConfigs.ToDictionary(gc => gc.GuildId, gc => new ConcurrentHashSet<string>(gc.FilteredWords.Select(fw => fw.Word)));
|
||||
var dict = guildConfigs.ToDictionary(gc => gc.GuildId, gc => new ConcurrentHashSet<string>(gc.FilteredWords.Select(fw => fw.Word)));
|
||||
|
||||
ServerFilteredWords = new ConcurrentDictionary<ulong, ConcurrentHashSet<string>>(dict);
|
||||
serverFilteredWords = new ConcurrentDictionary<ulong, ConcurrentHashSet<string>>(dict);
|
||||
|
||||
var serverFiltering = guildConfigs.Where(gc => gc.FilterWords);
|
||||
WordFilteringServers = new ConcurrentHashSet<ulong>(serverFiltering.Select(gc => gc.GuildId));
|
||||
var serverFiltering = guildConfigs.Where(gc => gc.FilterWords);
|
||||
WordFilteringServers = new ConcurrentHashSet<ulong>(serverFiltering.Select(gc => gc.GuildId));
|
||||
|
||||
WordFilteringChannels = new ConcurrentHashSet<ulong>(guildConfigs.SelectMany(gc => gc.FilterWordsChannelIds.Select(fwci => fwci.ChannelId)));
|
||||
|
||||
}
|
||||
WordFilteringChannels = new ConcurrentHashSet<ulong>(guildConfigs.SelectMany(gc => gc.FilterWordsChannelIds.Select(fwci => fwci.ChannelId)));
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -78,12 +74,12 @@ namespace NadekoBot.Modules.Permissions
|
||||
if (enabled)
|
||||
{
|
||||
InviteFilteringServers.Add(channel.Guild.Id);
|
||||
await channel.SendConfirmAsync("Invite filtering enabled on this server.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("invite_filter_server_on").ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
InviteFilteringServers.TryRemove(channel.Guild.Id);
|
||||
await channel.SendConfirmAsync("Invite filtering disabled on this server.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("invite_filter_server_off").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -111,12 +107,11 @@ namespace NadekoBot.Modules.Permissions
|
||||
if (removed == 0)
|
||||
{
|
||||
InviteFilteringChannels.Add(channel.Id);
|
||||
await channel.SendConfirmAsync("Invite filtering enabled on this channel.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("invite_filter_channel_on").ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
InviteFilteringChannels.TryRemove(channel.Id);
|
||||
await channel.SendConfirmAsync("Invite filtering disabled on this channel.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("invite_filter_channel_off").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -137,12 +132,12 @@ namespace NadekoBot.Modules.Permissions
|
||||
if (enabled)
|
||||
{
|
||||
WordFilteringServers.Add(channel.Guild.Id);
|
||||
await channel.SendConfirmAsync("Word filtering enabled on this server.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("word_filter_server_on").ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
WordFilteringServers.TryRemove(channel.Guild.Id);
|
||||
await channel.SendConfirmAsync("Word filtering disabled on this server.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("word_filter_server_off").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -170,12 +165,12 @@ namespace NadekoBot.Modules.Permissions
|
||||
if (removed == 0)
|
||||
{
|
||||
WordFilteringChannels.Add(channel.Id);
|
||||
await channel.SendConfirmAsync("Word filtering enabled on this channel.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("word_filter_channel_on").ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
WordFilteringChannels.TryRemove(channel.Id);
|
||||
await channel.SendConfirmAsync("Word filtering disabled on this channel.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("word_filter_channel_off").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -203,19 +198,17 @@ namespace NadekoBot.Modules.Permissions
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
var filteredWords = ServerFilteredWords.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet<string>());
|
||||
var filteredWords = serverFilteredWords.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet<string>());
|
||||
|
||||
if (removed == 0)
|
||||
{
|
||||
filteredWords.Add(word);
|
||||
await channel.SendConfirmAsync($"Word `{word}` successfully added to the list of filtered words.")
|
||||
.ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("filter_word_add", Format.Code(word)).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
filteredWords.TryRemove(word);
|
||||
await channel.SendConfirmAsync($"Word `{word}` removed from the list of filtered words.")
|
||||
.ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("filter_word_remove", Format.Code(word)).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -226,9 +219,9 @@ namespace NadekoBot.Modules.Permissions
|
||||
var channel = (ITextChannel)Context.Channel;
|
||||
|
||||
ConcurrentHashSet<string> filteredWords;
|
||||
ServerFilteredWords.TryGetValue(channel.Guild.Id, out filteredWords);
|
||||
serverFilteredWords.TryGetValue(channel.Guild.Id, out filteredWords);
|
||||
|
||||
await channel.SendConfirmAsync($"List of filtered words", string.Join("\n", filteredWords))
|
||||
await channel.SendConfirmAsync(GetText("filter_word_list"), string.Join("\n", filteredWords))
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ using NLog;
|
||||
namespace NadekoBot.Modules.Permissions
|
||||
{
|
||||
[NadekoModule("Permissions", ";")]
|
||||
public partial class Permissions : DiscordModule
|
||||
public partial class Permissions : NadekoTopLevelModule
|
||||
{
|
||||
public class PermissionCache
|
||||
{
|
||||
@ -29,7 +29,7 @@ namespace NadekoBot.Modules.Permissions
|
||||
|
||||
static Permissions()
|
||||
{
|
||||
var _log = LogManager.GetCurrentClassLogger();
|
||||
var log = LogManager.GetCurrentClassLogger();
|
||||
var sw = Stopwatch.StartNew();
|
||||
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
@ -46,7 +46,7 @@ namespace NadekoBot.Modules.Permissions
|
||||
}
|
||||
|
||||
sw.Stop();
|
||||
_log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s");
|
||||
log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s");
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -65,8 +65,14 @@ namespace NadekoBot.Modules.Permissions
|
||||
}, (id, old) => { old.Verbose = config.VerbosePermissions; return old; });
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await Context.Channel.SendConfirmAsync("ℹ️ I will " + (action.Value ? "now" : "no longer") + " show permission warnings.").ConfigureAwait(false);
|
||||
if (action.Value)
|
||||
{
|
||||
await ReplyConfirmLocalized("verbose_true").ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyConfirmLocalized("verbose_false").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -81,22 +87,20 @@ namespace NadekoBot.Modules.Permissions
|
||||
var config = uow.GuildConfigs.For(Context.Guild.Id, set => set);
|
||||
if (role == null)
|
||||
{
|
||||
await Context.Channel.SendConfirmAsync($"ℹ️ Current permission role is **{config.PermissionRole}**.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("permrole", Format.Bold(config.PermissionRole)).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
config.PermissionRole = role.Name.Trim();
|
||||
Cache.AddOrUpdate(Context.Guild.Id, new PermissionCache()
|
||||
{
|
||||
PermRole = config.PermissionRole,
|
||||
RootPermission = Permission.GetDefaultRoot(),
|
||||
Verbose = config.VerbosePermissions
|
||||
}, (id, old) => { old.PermRole = role.Name.Trim(); return old; });
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
config.PermissionRole = role.Name.Trim();
|
||||
Cache.AddOrUpdate(Context.Guild.Id, new PermissionCache()
|
||||
{
|
||||
PermRole = config.PermissionRole,
|
||||
RootPermission = Permission.GetDefaultRoot(),
|
||||
Verbose = config.VerbosePermissions
|
||||
}, (id, old) => { old.PermRole = role.Name.Trim(); return old; });
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await Context.Channel.SendConfirmAsync($"Users now require **{role.Name}** role in order to edit permissions.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("permrole_changed", Format.Bold(role.Name)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -105,12 +109,18 @@ namespace NadekoBot.Modules.Permissions
|
||||
{
|
||||
if (page < 1 || page > 4)
|
||||
return;
|
||||
string toSend = "";
|
||||
string toSend;
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
{
|
||||
var perms = uow.GuildConfigs.PermissionsFor(Context.Guild.Id).RootPermission;
|
||||
var i = 1 + 20 * (page - 1);
|
||||
toSend = Format.Code($"📄 Permissions page {page}") + "\n\n" + String.Join("\n", perms.AsEnumerable().Skip((page - 1) * 20).Take(20).Select(p => $"`{(i++)}.` {(p.Next == null ? Format.Bold(p.GetCommand((SocketGuild)Context.Guild) + " [uneditable]") : (p.GetCommand((SocketGuild)Context.Guild)))}"));
|
||||
toSend = Format.Bold(GetText("page", page)) + "\n\n" + string.Join("\n",
|
||||
perms.AsEnumerable()
|
||||
.Skip((page - 1) * 20)
|
||||
.Take(20)
|
||||
.Select(
|
||||
p =>
|
||||
$"`{(i++)}.` {(p.Next == null ? Format.Bold(p.GetCommand((SocketGuild) Context.Guild) + $" [{GetText("uneditable")}]") : (p.GetCommand((SocketGuild) Context.Guild)))}"));
|
||||
}
|
||||
|
||||
await Context.Channel.SendMessageAsync(toSend).ConfigureAwait(false);
|
||||
@ -132,7 +142,7 @@ namespace NadekoBot.Modules.Permissions
|
||||
{
|
||||
return;
|
||||
}
|
||||
else if (index == 0)
|
||||
if (index == 0)
|
||||
{
|
||||
p = perms;
|
||||
config.RootPermission = perms.Next;
|
||||
@ -155,12 +165,13 @@ namespace NadekoBot.Modules.Permissions
|
||||
uow2._context.Remove<Permission>(p);
|
||||
uow2._context.SaveChanges();
|
||||
}
|
||||
|
||||
await Context.Channel.SendConfirmAsync($"✅ {Context.User.Mention} removed permission **{p.GetCommand((SocketGuild)Context.Guild)}** from position #{index + 1}.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("removed",
|
||||
index+1,
|
||||
Format.Code(p.GetCommand((SocketGuild)Context.Guild))).ConfigureAwait(false);
|
||||
}
|
||||
catch (ArgumentOutOfRangeException)
|
||||
catch (IndexOutOfRangeException)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("❗️`No command on that index found.`").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("perm_out_of_range").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -180,7 +191,6 @@ namespace NadekoBot.Modules.Permissions
|
||||
{
|
||||
var config = uow.GuildConfigs.PermissionsFor(Context.Guild.Id);
|
||||
var perms = config.RootPermission;
|
||||
var root = perms;
|
||||
var index = 0;
|
||||
var fromFound = false;
|
||||
var toFound = false;
|
||||
@ -207,13 +217,13 @@ namespace NadekoBot.Modules.Permissions
|
||||
{
|
||||
if (!fromFound)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync($"Can't find permission at index `#{++from}`").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("not_found", ++from).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!toFound)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync($"Can't find permission at index `#{++to}`").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("not_found", ++to).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -230,7 +240,6 @@ namespace NadekoBot.Modules.Permissions
|
||||
next.Previous = pre;
|
||||
if (from == 0)
|
||||
{
|
||||
root = next;
|
||||
}
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
//Inserting
|
||||
@ -263,14 +272,18 @@ namespace NadekoBot.Modules.Permissions
|
||||
}, (id, old) => { old.RootPermission = config.RootPermission; return old; });
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
await Context.Channel.SendConfirmAsync($"`Moved permission:` \"{fromPerm.GetCommand((SocketGuild)Context.Guild)}\" `from #{++from} to #{++to}.`").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("moved_permission",
|
||||
Format.Code(fromPerm.GetCommand((SocketGuild) Context.Guild)),
|
||||
++from,
|
||||
++to)
|
||||
.ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
catch (Exception e) when (e is ArgumentOutOfRangeException || e is IndexOutOfRangeException)
|
||||
{
|
||||
}
|
||||
}
|
||||
await Context.Channel.SendErrorAsync("`Invalid index(es) specified.`").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("perm_out_of_range").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -297,7 +310,19 @@ namespace NadekoBot.Modules.Permissions
|
||||
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
await Context.Channel.SendConfirmAsync($"{(action.Value ? "✅ Allowed" : "🆗 Denied")} usage of `{command.Aliases.First()}` command on this server.").ConfigureAwait(false);
|
||||
|
||||
if (action.Value)
|
||||
{
|
||||
await ReplyConfirmLocalized("sx_enable",
|
||||
Format.Code(command.Aliases.First()),
|
||||
GetText("of_command")).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyConfirmLocalized("sx_disable",
|
||||
Format.Code(command.Aliases.First()),
|
||||
GetText("of_command")).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -323,7 +348,19 @@ namespace NadekoBot.Modules.Permissions
|
||||
}, (id, old) => { old.RootPermission = config.RootPermission; return old; });
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
await Context.Channel.SendConfirmAsync($"{(action.Value ? "✅ Allowed" : "🆗 Denied")} usage of **`{module.Name}`** module on this server.").ConfigureAwait(false);
|
||||
|
||||
if (action.Value)
|
||||
{
|
||||
await ReplyConfirmLocalized("sx_enable",
|
||||
Format.Code(module.Name),
|
||||
GetText("of_module")).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyConfirmLocalized("sx_disable",
|
||||
Format.Code(module.Name),
|
||||
GetText("of_module")).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -349,7 +386,21 @@ namespace NadekoBot.Modules.Permissions
|
||||
}, (id, old) => { old.RootPermission = config.RootPermission; return old; });
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
await Context.Channel.SendConfirmAsync($"{(action.Value ? "✅ Allowed" : "🆗 Denied")} usage of `{command.Aliases.First()}` command for `{user}` user.").ConfigureAwait(false);
|
||||
|
||||
if (action.Value)
|
||||
{
|
||||
await ReplyConfirmLocalized("ux_enable",
|
||||
Format.Code(command.Aliases.First()),
|
||||
GetText("of_command"),
|
||||
Format.Code(user.ToString())).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyConfirmLocalized("ux_disable",
|
||||
Format.Code(command.Aliases.First()),
|
||||
GetText("of_command"),
|
||||
Format.Code(user.ToString())).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -375,7 +426,21 @@ namespace NadekoBot.Modules.Permissions
|
||||
}, (id, old) => { old.RootPermission = config.RootPermission; return old; });
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
await Context.Channel.SendConfirmAsync($"{(action.Value ? "✅ Allowed" : "🆗 Denied")} usage of `{module.Name}` module for `{user}` user.").ConfigureAwait(false);
|
||||
|
||||
if (action.Value)
|
||||
{
|
||||
await ReplyConfirmLocalized("ux_enable",
|
||||
Format.Code(module.Name),
|
||||
GetText("of_module"),
|
||||
Format.Code(user.ToString())).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyConfirmLocalized("ux_disable",
|
||||
Format.Code(module.Name),
|
||||
GetText("of_module"),
|
||||
Format.Code(user.ToString())).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -404,7 +469,21 @@ namespace NadekoBot.Modules.Permissions
|
||||
}, (id, old) => { old.RootPermission = config.RootPermission; return old; });
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
await Context.Channel.SendConfirmAsync($"{(action.Value ? "✅ Allowed" : "🆗 Denied")} usage of `{command.Aliases.First()}` command for `{role}` role.").ConfigureAwait(false);
|
||||
|
||||
if (action.Value)
|
||||
{
|
||||
await ReplyConfirmLocalized("rx_enable",
|
||||
Format.Code(command.Aliases.First()),
|
||||
GetText("of_command"),
|
||||
Format.Code(role.Name)).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyConfirmLocalized("rx_disable",
|
||||
Format.Code(command.Aliases.First()),
|
||||
GetText("of_command"),
|
||||
Format.Code(role.Name)).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -433,39 +512,62 @@ namespace NadekoBot.Modules.Permissions
|
||||
}, (id, old) => { old.RootPermission = config.RootPermission; return old; });
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
await Context.Channel.SendConfirmAsync($"{(action.Value ? "✅ Allowed" : "🆗 Denied")} usage of `{module.Name}` module for `{role}` role.").ConfigureAwait(false);
|
||||
|
||||
|
||||
if (action.Value)
|
||||
{
|
||||
await ReplyConfirmLocalized("rx_enable",
|
||||
Format.Code(module.Name),
|
||||
GetText("of_module"),
|
||||
Format.Code(role.Name)).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyConfirmLocalized("rx_disable",
|
||||
Format.Code(module.Name),
|
||||
GetText("of_module"),
|
||||
Format.Code(role.Name)).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task ChnlCmd(CommandInfo command, PermissionAction action, [Remainder] ITextChannel chnl)
|
||||
{
|
||||
try
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
{
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
var newPerm = new Permission
|
||||
{
|
||||
var newPerm = new Permission
|
||||
{
|
||||
PrimaryTarget = PrimaryPermissionType.Channel,
|
||||
PrimaryTargetId = chnl.Id,
|
||||
SecondaryTarget = SecondaryPermissionType.Command,
|
||||
SecondaryTargetName = command.Aliases.First().ToLowerInvariant(),
|
||||
State = action.Value,
|
||||
};
|
||||
var config = uow.GuildConfigs.SetNewRootPermission(Context.Guild.Id, newPerm);
|
||||
Cache.AddOrUpdate(Context.Guild.Id, new PermissionCache()
|
||||
{
|
||||
PermRole = config.PermissionRole,
|
||||
RootPermission = config.RootPermission,
|
||||
Verbose = config.VerbosePermissions
|
||||
}, (id, old) => { old.RootPermission = config.RootPermission; return old; });
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
PrimaryTarget = PrimaryPermissionType.Channel,
|
||||
PrimaryTargetId = chnl.Id,
|
||||
SecondaryTarget = SecondaryPermissionType.Command,
|
||||
SecondaryTargetName = command.Aliases.First().ToLowerInvariant(),
|
||||
State = action.Value,
|
||||
};
|
||||
var config = uow.GuildConfigs.SetNewRootPermission(Context.Guild.Id, newPerm);
|
||||
Cache.AddOrUpdate(Context.Guild.Id, new PermissionCache()
|
||||
{
|
||||
PermRole = config.PermissionRole,
|
||||
RootPermission = config.RootPermission,
|
||||
Verbose = config.VerbosePermissions
|
||||
}, (id, old) => { old.RootPermission = config.RootPermission; return old; });
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
_log.Error(ex);
|
||||
|
||||
if (action.Value)
|
||||
{
|
||||
await ReplyConfirmLocalized("cx_enable",
|
||||
Format.Code(command.Aliases.First()),
|
||||
GetText("of_command"),
|
||||
Format.Code(chnl.Name)).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyConfirmLocalized("cx_disable",
|
||||
Format.Code(command.Aliases.First()),
|
||||
GetText("of_command"),
|
||||
Format.Code(chnl.Name)).ConfigureAwait(false);
|
||||
}
|
||||
await Context.Channel.SendConfirmAsync($"{(action.Value ? "✅ Allowed" : "🆗 Denied")} usage of `{command.Aliases.First()}` command for `{chnl}` channel.").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -491,7 +593,21 @@ namespace NadekoBot.Modules.Permissions
|
||||
}, (id, old) => { old.RootPermission = config.RootPermission; return old; });
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
await Context.Channel.SendConfirmAsync($"{(action.Value ? "✅ Allowed" : "🆗 Denied")} usage of `{module.Name}` module for `{chnl}` channel.").ConfigureAwait(false);
|
||||
|
||||
if (action.Value)
|
||||
{
|
||||
await ReplyConfirmLocalized("cx_enable",
|
||||
Format.Code(module.Name),
|
||||
GetText("of_module"),
|
||||
Format.Code(chnl.Name)).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyConfirmLocalized("cx_disable",
|
||||
Format.Code(module.Name),
|
||||
GetText("of_module"),
|
||||
Format.Code(chnl.Name)).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -517,7 +633,17 @@ namespace NadekoBot.Modules.Permissions
|
||||
}, (id, old) => { old.RootPermission = config.RootPermission; return old; });
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
await Context.Channel.SendConfirmAsync($"{(action.Value ? "✅ Allowed" : "🆗 Denied")} usage of `ALL MODULES` for `{chnl}` channel.").ConfigureAwait(false);
|
||||
|
||||
if (action.Value)
|
||||
{
|
||||
await ReplyConfirmLocalized("acm_enable",
|
||||
Format.Code(chnl.Name)).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyConfirmLocalized("acm_disable",
|
||||
Format.Code(chnl.Name)).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -546,7 +672,17 @@ namespace NadekoBot.Modules.Permissions
|
||||
}, (id, old) => { old.RootPermission = config.RootPermission; return old; });
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
await Context.Channel.SendConfirmAsync($"{(action.Value ? "✅ Allowed" : "🆗 Denied")} usage of `ALL MODULES` for `{role}` role.").ConfigureAwait(false);
|
||||
|
||||
if (action.Value)
|
||||
{
|
||||
await ReplyConfirmLocalized("arm_enable",
|
||||
Format.Code(role.Name)).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyConfirmLocalized("arm_disable",
|
||||
Format.Code(role.Name)).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -572,7 +708,17 @@ namespace NadekoBot.Modules.Permissions
|
||||
}, (id, old) => { old.RootPermission = config.RootPermission; return old; });
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
await Context.Channel.SendConfirmAsync($"{(action.Value ? "✅ Allowed" : "🆗 Denied")} usage of `ALL MODULES` for `{user}` user.").ConfigureAwait(false);
|
||||
|
||||
if (action.Value)
|
||||
{
|
||||
await ReplyConfirmLocalized("aum_enable",
|
||||
Format.Code(user.ToString())).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyConfirmLocalized("aum_disable",
|
||||
Format.Code(user.ToString())).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -609,7 +755,15 @@ namespace NadekoBot.Modules.Permissions
|
||||
}, (id, old) => { old.RootPermission = config.RootPermission; return old; });
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
await Context.Channel.SendConfirmAsync($"{(action.Value ? "✅ Allowed" : "🆗 Denied")} usage of `ALL MODULES` on this server.").ConfigureAwait(false);
|
||||
|
||||
if (action.Value)
|
||||
{
|
||||
await ReplyConfirmLocalized("asm_enable").ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyConfirmLocalized("asm_disable").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,21 +16,21 @@ using System.Collections.Concurrent;
|
||||
namespace NadekoBot.Modules.Pokemon
|
||||
{
|
||||
[NadekoModule("Pokemon", ">")]
|
||||
public partial class Pokemon : DiscordModule
|
||||
public class Pokemon : NadekoTopLevelModule
|
||||
{
|
||||
private static List<PokemonType> PokemonTypes = new List<PokemonType>();
|
||||
private static ConcurrentDictionary<ulong, PokeStats> Stats = new ConcurrentDictionary<ulong, PokeStats>();
|
||||
private static readonly List<PokemonType> _pokemonTypes = new List<PokemonType>();
|
||||
private static readonly ConcurrentDictionary<ulong, PokeStats> _stats = new ConcurrentDictionary<ulong, PokeStats>();
|
||||
|
||||
public const string PokemonTypesFile = "data/pokemon_types.json";
|
||||
|
||||
private static new Logger _log { get; }
|
||||
private new static Logger _log { get; }
|
||||
|
||||
static Pokemon()
|
||||
{
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
if (File.Exists(PokemonTypesFile))
|
||||
{
|
||||
PokemonTypes = JsonConvert.DeserializeObject<List<PokemonType>>(File.ReadAllText(PokemonTypesFile));
|
||||
_pokemonTypes = JsonConvert.DeserializeObject<List<PokemonType>>(File.ReadAllText(PokemonTypesFile));
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -42,21 +42,18 @@ namespace NadekoBot.Modules.Pokemon
|
||||
private int GetDamage(PokemonType usertype, PokemonType targetType)
|
||||
{
|
||||
var rng = new Random();
|
||||
int damage = rng.Next(40, 60);
|
||||
foreach (PokemonMultiplier Multiplier in usertype.Multipliers)
|
||||
var damage = rng.Next(40, 60);
|
||||
foreach (var multiplierObj in usertype.Multipliers)
|
||||
{
|
||||
if (Multiplier.Type == targetType.Name)
|
||||
{
|
||||
var multiplier = Multiplier.Multiplication;
|
||||
damage = (int)(damage * multiplier);
|
||||
}
|
||||
if (multiplierObj.Type != targetType.Name) continue;
|
||||
damage = (int)(damage * multiplierObj.Multiplication);
|
||||
}
|
||||
|
||||
return damage;
|
||||
}
|
||||
|
||||
|
||||
private PokemonType GetPokeType(ulong id)
|
||||
private static PokemonType GetPokeType(ulong id)
|
||||
{
|
||||
|
||||
Dictionary<ulong, string> setTypes;
|
||||
@ -69,20 +66,18 @@ namespace NadekoBot.Modules.Pokemon
|
||||
{
|
||||
return StringToPokemonType(setTypes[id]);
|
||||
}
|
||||
int count = PokemonTypes.Count;
|
||||
var count = _pokemonTypes.Count;
|
||||
|
||||
int remainder = Math.Abs((int)(id % (ulong)count));
|
||||
var remainder = Math.Abs((int)(id % (ulong)count));
|
||||
|
||||
return PokemonTypes[remainder];
|
||||
return _pokemonTypes[remainder];
|
||||
}
|
||||
|
||||
|
||||
|
||||
private PokemonType StringToPokemonType(string v)
|
||||
|
||||
private static PokemonType StringToPokemonType(string v)
|
||||
{
|
||||
var str = v?.ToUpperInvariant();
|
||||
var list = PokemonTypes;
|
||||
foreach (PokemonType p in list)
|
||||
var list = _pokemonTypes;
|
||||
foreach (var p in list)
|
||||
{
|
||||
if (str == p.Name)
|
||||
{
|
||||
@ -91,8 +86,7 @@ namespace NadekoBot.Modules.Pokemon
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task Attack(string move, IGuildUser targetUser = null)
|
||||
@ -105,46 +99,44 @@ namespace NadekoBot.Modules.Pokemon
|
||||
|
||||
if (targetUser == null)
|
||||
{
|
||||
await Context.Channel.SendMessageAsync("No such person.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("user_not_found").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
else if (targetUser == user)
|
||||
if (targetUser == user)
|
||||
{
|
||||
await Context.Channel.SendMessageAsync("You can't attack yourself.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("cant_attack_yourself").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Checking stats first, then move
|
||||
//Set up the userstats
|
||||
PokeStats userStats;
|
||||
userStats = Stats.GetOrAdd(user.Id, new PokeStats());
|
||||
var userStats = _stats.GetOrAdd(user.Id, new PokeStats());
|
||||
|
||||
//Check if able to move
|
||||
//User not able if HP < 0, has made more than 4 attacks
|
||||
if (userStats.Hp < 0)
|
||||
{
|
||||
await Context.Channel.SendMessageAsync($"{user.Mention} has fainted and was not able to move!").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("you_fainted").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
if (userStats.MovesMade >= 5)
|
||||
{
|
||||
await Context.Channel.SendMessageAsync($"{user.Mention} has used too many moves in a row and was not able to move!").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("too_many_moves").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
if (userStats.LastAttacked.Contains(targetUser.Id))
|
||||
{
|
||||
await Context.Channel.SendMessageAsync($"{user.Mention} can't attack again without retaliation!").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("cant_attack_again").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
//get target stats
|
||||
PokeStats targetStats;
|
||||
targetStats = Stats.GetOrAdd(targetUser.Id, new PokeStats());
|
||||
var targetStats = _stats.GetOrAdd(targetUser.Id, new PokeStats());
|
||||
|
||||
//If target's HP is below 0, no use attacking
|
||||
if (targetStats.Hp <= 0)
|
||||
{
|
||||
await Context.Channel.SendMessageAsync($"{targetUser.Mention} has already fainted!").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("too_many_moves", targetUser).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -154,7 +146,7 @@ namespace NadekoBot.Modules.Pokemon
|
||||
var enabledMoves = userType.Moves;
|
||||
if (!enabledMoves.Contains(move.ToLowerInvariant()))
|
||||
{
|
||||
await Context.Channel.SendMessageAsync($"{user.Mention} is not able to use **{move}**. Type {NadekoBot.ModulePrefixes[typeof(Pokemon).Name]}ml to see moves").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("invalid_move", Format.Bold(move), Prefix).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -164,32 +156,32 @@ namespace NadekoBot.Modules.Pokemon
|
||||
int damage = GetDamage(userType, targetType);
|
||||
//apply damage to target
|
||||
targetStats.Hp -= damage;
|
||||
|
||||
var response = $"{user.Mention} used **{move}**{userType.Icon} on {targetUser.Mention}{targetType.Icon} for **{damage}** damage";
|
||||
|
||||
var response = GetText("attack", Format.Bold(move), userType.Icon, Format.Bold(targetUser.ToString()), targetType.Icon, Format.Bold(damage.ToString()));
|
||||
|
||||
//Damage type
|
||||
if (damage < 40)
|
||||
{
|
||||
response += "\nIt's not effective..";
|
||||
response += "\n" + GetText("not_effective");
|
||||
}
|
||||
else if (damage > 60)
|
||||
{
|
||||
response += "\nIt's super effective!";
|
||||
response += "\n" + GetText("super_effective");
|
||||
}
|
||||
else
|
||||
{
|
||||
response += "\nIt's somewhat effective";
|
||||
response += "\n" + GetText("somewhat_effective");
|
||||
}
|
||||
|
||||
//check fainted
|
||||
|
||||
if (targetStats.Hp <= 0)
|
||||
{
|
||||
response += $"\n**{targetUser.Mention}** has fainted!";
|
||||
response += "\n" + GetText("fainted", Format.Bold(targetUser.ToString()));
|
||||
}
|
||||
else
|
||||
{
|
||||
response += $"\n**{targetUser.Mention}** has {targetStats.Hp} HP remaining";
|
||||
response += "\n" + GetText("hp_remaining", Format.Bold(targetUser.ToString()), targetStats.Hp);
|
||||
}
|
||||
|
||||
//update other stats
|
||||
@ -203,10 +195,10 @@ namespace NadekoBot.Modules.Pokemon
|
||||
|
||||
//update dictionary
|
||||
//This can stay the same right?
|
||||
Stats[user.Id] = userStats;
|
||||
Stats[targetUser.Id] = targetStats;
|
||||
_stats[user.Id] = userStats;
|
||||
_stats[targetUser.Id] = targetStats;
|
||||
|
||||
await Context.Channel.SendMessageAsync(response).ConfigureAwait(false);
|
||||
await Context.Channel.SendConfirmAsync(Context.User.Mention + " " + response).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
|
||||
@ -218,12 +210,10 @@ namespace NadekoBot.Modules.Pokemon
|
||||
|
||||
var userType = GetPokeType(user.Id);
|
||||
var movesList = userType.Moves;
|
||||
var str = $"**Moves for `{userType.Name}` type.**";
|
||||
foreach (string m in movesList)
|
||||
{
|
||||
str += $"\n{userType.Icon}{m}";
|
||||
}
|
||||
await Context.Channel.SendMessageAsync(str).ConfigureAwait(false);
|
||||
var embed = new EmbedBuilder().WithOkColor()
|
||||
.WithTitle(GetText("moves", userType))
|
||||
.WithDescription(string.Join("\n", movesList.Select(m => userType.Icon + " " + m)));
|
||||
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -232,17 +222,18 @@ namespace NadekoBot.Modules.Pokemon
|
||||
{
|
||||
IGuildUser user = (IGuildUser)Context.User;
|
||||
|
||||
if (targetUser == null) {
|
||||
await Context.Channel.SendMessageAsync("No such person.").ConfigureAwait(false);
|
||||
if (targetUser == null)
|
||||
{
|
||||
await ReplyErrorLocalized("user_not_found").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Stats.ContainsKey(targetUser.Id))
|
||||
if (_stats.ContainsKey(targetUser.Id))
|
||||
{
|
||||
var targetStats = Stats[targetUser.Id];
|
||||
var targetStats = _stats[targetUser.Id];
|
||||
if (targetStats.Hp == targetStats.MaxHp)
|
||||
{
|
||||
await Context.Channel.SendMessageAsync($"{targetUser.Mention} already has full HP!").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("already_full", Format.Bold(targetUser.ToString())).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
//Payment~
|
||||
@ -251,11 +242,11 @@ namespace NadekoBot.Modules.Pokemon
|
||||
var target = (targetUser.Id == user.Id) ? "yourself" : targetUser.Mention;
|
||||
if (amount > 0)
|
||||
{
|
||||
if (!await CurrencyHandler.RemoveCurrencyAsync(user, $"Poke-Heal {target}", amount, true).ConfigureAwait(false))
|
||||
{
|
||||
try { await Context.Channel.SendMessageAsync($"{user.Mention} You don't have enough {NadekoBot.BotConfig.CurrencyName}s.").ConfigureAwait(false); } catch { }
|
||||
return;
|
||||
}
|
||||
if (!await CurrencyHandler.RemoveCurrencyAsync(user, $"Poke-Heal {target}", amount, true).ConfigureAwait(false))
|
||||
{
|
||||
await ReplyErrorLocalized("no_currency", NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//healing
|
||||
@ -263,23 +254,20 @@ namespace NadekoBot.Modules.Pokemon
|
||||
if (targetStats.Hp < 0)
|
||||
{
|
||||
//Could heal only for half HP?
|
||||
Stats[targetUser.Id].Hp = (targetStats.MaxHp / 2);
|
||||
_stats[targetUser.Id].Hp = (targetStats.MaxHp / 2);
|
||||
if (target == "yourself")
|
||||
{
|
||||
await Context.Channel.SendMessageAsync($"You revived yourself with one {NadekoBot.BotConfig.CurrencySign}").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("revive_yourself", NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
await Context.Channel.SendMessageAsync($"{user.Mention} revived {targetUser.Mention} with one {NadekoBot.BotConfig.CurrencySign}").ConfigureAwait(false);
|
||||
}
|
||||
return;
|
||||
|
||||
await ReplyConfirmLocalized("revive_other", Format.Bold(targetUser.ToString()), NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false);
|
||||
}
|
||||
await Context.Channel.SendMessageAsync($"{user.Mention} healed {targetUser.Mention} with one {NadekoBot.BotConfig.CurrencySign}").ConfigureAwait(false);
|
||||
return;
|
||||
await ReplyConfirmLocalized("healed", Format.Bold(targetUser.ToString()), NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await Context.Channel.SendMessageAsync($"{targetUser.Mention} already has full HP!").ConfigureAwait(false);
|
||||
await ErrorLocalized("already_full", Format.Bold(targetUser.ToString()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -288,15 +276,9 @@ namespace NadekoBot.Modules.Pokemon
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task Type(IGuildUser targetUser = null)
|
||||
{
|
||||
IGuildUser user = (IGuildUser)Context.User;
|
||||
|
||||
if (targetUser == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
targetUser = targetUser ?? (IGuildUser)Context.User;
|
||||
var pType = GetPokeType(targetUser.Id);
|
||||
await Context.Channel.SendMessageAsync($"Type of {targetUser.Mention} is **{pType.Name.ToLowerInvariant()}**{pType.Icon}").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("type_of_user", Format.Bold(targetUser.ToString()), pType).ConfigureAwait(false);
|
||||
|
||||
}
|
||||
|
||||
@ -309,7 +291,7 @@ namespace NadekoBot.Modules.Pokemon
|
||||
var targetType = StringToPokemonType(typeTargeted);
|
||||
if (targetType == null)
|
||||
{
|
||||
await Context.Channel.EmbedAsync(PokemonTypes.Aggregate(new EmbedBuilder().WithDescription("List of the available types:"),
|
||||
await Context.Channel.EmbedAsync(_pokemonTypes.Aggregate(new EmbedBuilder().WithDescription("List of the available types:"),
|
||||
(eb, pt) => eb.AddField(efb => efb.WithName(pt.Name)
|
||||
.WithValue(pt.Icon)
|
||||
.WithIsInline(true)))
|
||||
@ -318,7 +300,7 @@ namespace NadekoBot.Modules.Pokemon
|
||||
}
|
||||
if (targetType == GetPokeType(user.Id))
|
||||
{
|
||||
await Context.Channel.SendMessageAsync($"Your type is already {targetType.Name.ToLowerInvariant()}{targetType.Icon}").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("already_that_type", targetType).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -326,20 +308,19 @@ namespace NadekoBot.Modules.Pokemon
|
||||
var amount = 1;
|
||||
if (amount > 0)
|
||||
{
|
||||
if (!await CurrencyHandler.RemoveCurrencyAsync(user, $"{user.Mention} change type to {typeTargeted}", amount, true).ConfigureAwait(false))
|
||||
if (!await CurrencyHandler.RemoveCurrencyAsync(user, $"{user} change type to {typeTargeted}", amount, true).ConfigureAwait(false))
|
||||
{
|
||||
try { await Context.Channel.SendMessageAsync($"{user.Mention} You don't have enough {NadekoBot.BotConfig.CurrencyName}s.").ConfigureAwait(false); } catch { }
|
||||
await ReplyErrorLocalized("no_currency", NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//Actually changing the type here
|
||||
Dictionary<ulong, string> setTypes;
|
||||
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
{
|
||||
var pokeUsers = uow.PokeGame.GetAll();
|
||||
setTypes = pokeUsers.ToDictionary(x => x.UserId, y => y.type);
|
||||
var pokeUsers = uow.PokeGame.GetAll().ToArray();
|
||||
var setTypes = pokeUsers.ToDictionary(x => x.UserId, y => y.type);
|
||||
var pt = new UserPokeTypes
|
||||
{
|
||||
UserId = user.Id,
|
||||
@ -353,7 +334,7 @@ namespace NadekoBot.Modules.Pokemon
|
||||
else
|
||||
{
|
||||
//update user in db
|
||||
var pokeUserCmd = pokeUsers.Where(p => p.UserId == user.Id).FirstOrDefault();
|
||||
var pokeUserCmd = pokeUsers.FirstOrDefault(p => p.UserId == user.Id);
|
||||
pokeUserCmd.type = targetType.Name;
|
||||
uow.PokeGame.Update(pokeUserCmd);
|
||||
}
|
||||
@ -361,9 +342,10 @@ namespace NadekoBot.Modules.Pokemon
|
||||
}
|
||||
|
||||
//Now for the response
|
||||
await Context.Channel.SendMessageAsync($"Set type of {user.Mention} to {typeTargeted}{targetType.Icon} for a {NadekoBot.BotConfig.CurrencySign}").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("settype_success",
|
||||
targetType,
|
||||
NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,9 @@ namespace NadekoBot.Modules.Pokemon
|
||||
public List<PokemonMultiplier> Multipliers { get; set; }
|
||||
public string Icon { get; set; }
|
||||
public string[] Moves { get; set; }
|
||||
|
||||
public override string ToString() =>
|
||||
Icon + "**" + Name.ToLowerInvariant() + "**" + Icon;
|
||||
}
|
||||
public class PokemonMultiplier
|
||||
{
|
||||
|
@ -1,6 +1,5 @@
|
||||
using AngleSharp;
|
||||
using AngleSharp.Dom.Html;
|
||||
using AngleSharp.Extensions;
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using NadekoBot.Attributes;
|
||||
@ -8,7 +7,6 @@ using NadekoBot.Extensions;
|
||||
using NadekoBot.Modules.Searches.Models;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@ -21,37 +19,37 @@ namespace NadekoBot.Modules.Searches
|
||||
public partial class Searches
|
||||
{
|
||||
[Group]
|
||||
public class AnimeSearchCommands : ModuleBase
|
||||
public class AnimeSearchCommands : NadekoSubmodule
|
||||
{
|
||||
private static Timer anilistTokenRefresher { get; }
|
||||
private static Logger _log { get; }
|
||||
private static readonly Timer anilistTokenRefresher;
|
||||
private static string anilistToken { get; set; }
|
||||
|
||||
static AnimeSearchCommands()
|
||||
{
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
anilistTokenRefresher = new Timer(async (state) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var headers = new Dictionary<string, string> {
|
||||
{"grant_type", "client_credentials"},
|
||||
{"client_id", "kwoth-w0ki9"},
|
||||
{"client_secret", "Qd6j4FIAi1ZK6Pc7N7V4Z"},
|
||||
};
|
||||
var headers = new Dictionary<string, string>
|
||||
{
|
||||
{"grant_type", "client_credentials"},
|
||||
{"client_id", "kwoth-w0ki9"},
|
||||
{"client_secret", "Qd6j4FIAi1ZK6Pc7N7V4Z"},
|
||||
};
|
||||
|
||||
using (var http = new HttpClient())
|
||||
{
|
||||
http.AddFakeHeaders();
|
||||
//http.AddFakeHeaders();
|
||||
http.DefaultRequestHeaders.Clear();
|
||||
var formContent = new FormUrlEncodedContent(headers);
|
||||
var response = await http.PostAsync("http://anilist.co/api/auth/access_token", formContent).ConfigureAwait(false);
|
||||
var response = await http.PostAsync("https://anilist.co/api/auth/access_token", formContent).ConfigureAwait(false);
|
||||
var stringContent = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
anilistToken = JObject.Parse(stringContent)["access_token"].ToString();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
catch
|
||||
{
|
||||
_log.Error(ex);
|
||||
// ignored
|
||||
}
|
||||
}, null, TimeSpan.FromSeconds(0), TimeSpan.FromMinutes(29));
|
||||
}
|
||||
@ -75,7 +73,7 @@ namespace NadekoBot.Modules.Searches
|
||||
|
||||
var favorites = document.QuerySelectorAll("div.user-favorites > div.di-tc");
|
||||
|
||||
var favAnime = "No favorite anime yet";
|
||||
var favAnime = GetText("anime_no_fav");
|
||||
if (favorites[0].QuerySelector("p") == null)
|
||||
favAnime = string.Join("\n", favorites[0].QuerySelectorAll("ul > li > div.di-tc.va-t > a")
|
||||
.Shuffle()
|
||||
@ -96,7 +94,7 @@ namespace NadekoBot.Modules.Searches
|
||||
// return $"[{elem.InnerHtml}]({elem.Href})";
|
||||
// }));
|
||||
|
||||
var info = document.QuerySelectorAll("ul.user-status:nth-child(3) > li")
|
||||
var info = document.QuerySelectorAll("ul.user-status:nth-child(3) > li.clearfix")
|
||||
.Select(x => Tuple.Create(x.Children[0].InnerHtml, x.Children[1].InnerHtml))
|
||||
.ToList();
|
||||
|
||||
@ -106,14 +104,14 @@ namespace NadekoBot.Modules.Searches
|
||||
|
||||
var embed = new EmbedBuilder()
|
||||
.WithOkColor()
|
||||
.WithTitle($"{name}'s MAL profile")
|
||||
.AddField(efb => efb.WithName("💚 Watching").WithValue(stats[0]).WithIsInline(true))
|
||||
.AddField(efb => efb.WithName("💙 Completed").WithValue(stats[1]).WithIsInline(true));
|
||||
.WithTitle(GetText("mal_profile", name))
|
||||
.AddField(efb => efb.WithName("💚 " + GetText("watching")).WithValue(stats[0]).WithIsInline(true))
|
||||
.AddField(efb => efb.WithName("💙 " + GetText("completed")).WithValue(stats[1]).WithIsInline(true));
|
||||
if (info.Count < 3)
|
||||
embed.AddField(efb => efb.WithName("💛 On-Hold").WithValue(stats[2]).WithIsInline(true));
|
||||
embed.AddField(efb => efb.WithName("💛 " + GetText("on_hold")).WithValue(stats[2]).WithIsInline(true));
|
||||
embed
|
||||
.AddField(efb => efb.WithName("💔 Dropped").WithValue(stats[3]).WithIsInline(true))
|
||||
.AddField(efb => efb.WithName("⚪ Plan to watch").WithValue(stats[4]).WithIsInline(true))
|
||||
.AddField(efb => efb.WithName("💔 " + GetText("dropped")).WithValue(stats[3]).WithIsInline(true))
|
||||
.AddField(efb => efb.WithName("⚪ " + GetText("plan_to_watch")).WithValue(stats[4]).WithIsInline(true))
|
||||
.AddField(efb => efb.WithName("🕐 " + daysAndMean[0][0]).WithValue(daysAndMean[0][1]).WithIsInline(true))
|
||||
.AddField(efb => efb.WithName("📊 " + daysAndMean[1][0]).WithValue(daysAndMean[1][1]).WithIsInline(true))
|
||||
.AddField(efb => efb.WithName(MalInfoToEmoji(info[0].Item1) + " " + info[0].Item1).WithValue(info[0].Item2.TrimTo(20)).WithIsInline(true))
|
||||
@ -126,7 +124,7 @@ namespace NadekoBot.Modules.Searches
|
||||
.WithDescription($@"
|
||||
** https://myanimelist.net/animelist/{ name } **
|
||||
|
||||
**Top 3 Favorite Anime:**
|
||||
**{GetText("top_3_fav_anime")}**
|
||||
{favAnime}"
|
||||
|
||||
//**[Manga List](https://myanimelist.net/mangalist/{name})**
|
||||
@ -176,7 +174,7 @@ namespace NadekoBot.Modules.Searches
|
||||
|
||||
if (animeData == null)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("Failed finding that animu.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("failed_finding_anime").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -185,10 +183,10 @@ namespace NadekoBot.Modules.Searches
|
||||
.WithTitle(animeData.title_english)
|
||||
.WithUrl(animeData.Link)
|
||||
.WithImageUrl(animeData.image_url_lge)
|
||||
.AddField(efb => efb.WithName("Episodes").WithValue(animeData.total_episodes.ToString()).WithIsInline(true))
|
||||
.AddField(efb => efb.WithName("Status").WithValue(animeData.AiringStatus.ToString()).WithIsInline(true))
|
||||
.AddField(efb => efb.WithName("Genres").WithValue(String.Join(", ", animeData.Genres)).WithIsInline(true))
|
||||
.WithFooter(efb => efb.WithText("Score: " + animeData.average_score + " / 100"));
|
||||
.AddField(efb => efb.WithName(GetText("episodes")).WithValue(animeData.total_episodes.ToString()).WithIsInline(true))
|
||||
.AddField(efb => efb.WithName(GetText("status")).WithValue(animeData.AiringStatus.ToString()).WithIsInline(true))
|
||||
.AddField(efb => efb.WithName(GetText("genres")).WithValue(String.Join(",\n", animeData.Genres)).WithIsInline(true))
|
||||
.WithFooter(efb => efb.WithText(GetText("score") + " " + animeData.average_score + " / 100"));
|
||||
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@ -203,7 +201,7 @@ namespace NadekoBot.Modules.Searches
|
||||
|
||||
if (mangaData == null)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("Failed finding that mango.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("failed_finding_manga").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -212,10 +210,10 @@ namespace NadekoBot.Modules.Searches
|
||||
.WithTitle(mangaData.title_english)
|
||||
.WithUrl(mangaData.Link)
|
||||
.WithImageUrl(mangaData.image_url_lge)
|
||||
.AddField(efb => efb.WithName("Episodes").WithValue(mangaData.total_chapters.ToString()).WithIsInline(true))
|
||||
.AddField(efb => efb.WithName("Status").WithValue(mangaData.publishing_status.ToString()).WithIsInline(true))
|
||||
.AddField(efb => efb.WithName("Genres").WithValue(String.Join(", ", mangaData.Genres)).WithIsInline(true))
|
||||
.WithFooter(efb => efb.WithText("Score: " + mangaData.average_score + " / 100"));
|
||||
.AddField(efb => efb.WithName(GetText("chapters")).WithValue(mangaData.total_chapters.ToString()).WithIsInline(true))
|
||||
.AddField(efb => efb.WithName(GetText("status")).WithValue(mangaData.publishing_status.ToString()).WithIsInline(true))
|
||||
.AddField(efb => efb.WithName(GetText("genres")).WithValue(String.Join(",\n", mangaData.Genres)).WithIsInline(true))
|
||||
.WithFooter(efb => efb.WithText(GetText("score") + " " + mangaData.average_score + " / 100"));
|
||||
|
||||
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
|
||||
}
|
||||
@ -230,7 +228,7 @@ namespace NadekoBot.Modules.Searches
|
||||
var link = "http://anilist.co/api/anime/search/" + Uri.EscapeUriString(query);
|
||||
using (var http = new HttpClient())
|
||||
{
|
||||
var res = await http.GetStringAsync("http://anilist.co/api/anime/search/" + Uri.EscapeUriString(query) + $"?access_token={anilistToken}").ConfigureAwait(false);
|
||||
var res = await http.GetStringAsync(link + $"?access_token={anilistToken}").ConfigureAwait(false);
|
||||
var smallObj = JArray.Parse(res)[0];
|
||||
var aniData = await http.GetStringAsync("http://anilist.co/api/anime/" + smallObj["id"] + $"?access_token={anilistToken}").ConfigureAwait(false);
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
@ -13,7 +14,7 @@ namespace NadekoBot.Modules.Searches
|
||||
public static GoogleTranslator Instance = _instance ?? (_instance = new GoogleTranslator());
|
||||
|
||||
public IEnumerable<string> Languages => _languageDictionary.Keys.OrderBy(x => x);
|
||||
private Dictionary<string, string> _languageDictionary;
|
||||
private readonly Dictionary<string, string> _languageDictionary;
|
||||
|
||||
static GoogleTranslator() { }
|
||||
private GoogleTranslator() {
|
||||
@ -153,13 +154,18 @@ namespace NadekoBot.Modules.Searches
|
||||
|
||||
public async Task<string> Translate(string sourceText, string sourceLanguage, string targetLanguage)
|
||||
{
|
||||
string text = string.Empty;
|
||||
string text;
|
||||
|
||||
string url = string.Format("https://translate.googleapis.com/translate_a/single?client=gtx&sl={0}&tl={1}&dt=t&q={2}",
|
||||
if(!_languageDictionary.ContainsKey(sourceLanguage) ||
|
||||
!_languageDictionary.ContainsKey(targetLanguage))
|
||||
throw new ArgumentException();
|
||||
|
||||
|
||||
var url = string.Format("https://translate.googleapis.com/translate_a/single?client=gtx&sl={0}&tl={1}&dt=t&q={2}",
|
||||
ConvertToLanguageCode(sourceLanguage),
|
||||
ConvertToLanguageCode(targetLanguage),
|
||||
WebUtility.UrlEncode(sourceText));
|
||||
using (HttpClient http = new HttpClient())
|
||||
using (var http = new HttpClient())
|
||||
{
|
||||
http.DefaultRequestHeaders.Add("user-agent", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36");
|
||||
text = await http.GetStringAsync(url).ConfigureAwait(false);
|
||||
@ -170,7 +176,7 @@ namespace NadekoBot.Modules.Searches
|
||||
|
||||
private string ConvertToLanguageCode(string language)
|
||||
{
|
||||
string mode = string.Empty;
|
||||
string mode;
|
||||
_languageDictionary.TryGetValue(language, out mode);
|
||||
return mode;
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
@ -18,11 +17,11 @@ namespace NadekoBot.Modules.Searches
|
||||
public partial class Searches
|
||||
{
|
||||
[Group]
|
||||
public class JokeCommands : ModuleBase
|
||||
public class JokeCommands : NadekoSubmodule
|
||||
{
|
||||
private static List<WoWJoke> wowJokes { get; } = new List<WoWJoke>();
|
||||
private static List<MagicItem> magicItems { get; } = new List<MagicItem>();
|
||||
private static Logger _log { get; }
|
||||
private new static readonly Logger _log;
|
||||
|
||||
static JokeCommands()
|
||||
{
|
||||
@ -78,7 +77,7 @@ namespace NadekoBot.Modules.Searches
|
||||
{
|
||||
if (!wowJokes.Any())
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("Jokes not loaded.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("jokes_not_loaded").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
var joke = wowJokes[new NadekoRandom().Next(0, wowJokes.Count)];
|
||||
@ -90,7 +89,7 @@ namespace NadekoBot.Modules.Searches
|
||||
{
|
||||
if (!wowJokes.Any())
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("MagicItems not loaded.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("magicitems_not_loaded").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
var item = magicItems[new NadekoRandom().Next(0, magicItems.Count)];
|
||||
|
@ -33,7 +33,7 @@ namespace NadekoBot.Modules.Searches
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task Lolban()
|
||||
{
|
||||
var showCount = 8;
|
||||
const int showCount = 8;
|
||||
//http://api.champion.gg/stats/champs/mostBanned?api_key=YOUR_API_TOKEN&page=1&limit=2
|
||||
try
|
||||
{
|
||||
@ -44,19 +44,20 @@ namespace NadekoBot.Modules.Searches
|
||||
$"limit={showCount}")
|
||||
.ConfigureAwait(false))["data"] as JArray;
|
||||
var dataList = data.Distinct(new ChampionNameComparer()).Take(showCount).ToList();
|
||||
var eb = new EmbedBuilder().WithOkColor().WithTitle(Format.Underline($"{dataList.Count} most banned champions"));
|
||||
for (var i = 0; i < dataList.Count; i++)
|
||||
var eb = new EmbedBuilder().WithOkColor().WithTitle(Format.Underline(GetText("x_most_banned_champs",dataList.Count)));
|
||||
foreach (var champ in dataList)
|
||||
{
|
||||
var champ = dataList[i];
|
||||
eb.AddField(efb => efb.WithName(champ["name"].ToString()).WithValue(champ["general"]["banRate"] + "%").WithIsInline(true));
|
||||
var champ1 = champ;
|
||||
eb.AddField(efb => efb.WithName(champ1["name"].ToString()).WithValue(champ1["general"]["banRate"] + "%").WithIsInline(true));
|
||||
}
|
||||
|
||||
await Context.Channel.EmbedAsync(eb, Format.Italics(trashTalk[new NadekoRandom().Next(0, trashTalk.Length)])).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
catch (Exception ex)
|
||||
{
|
||||
await Context.Channel.SendMessageAsync("Something went wrong.").ConfigureAwait(false);
|
||||
_log.Warn(ex);
|
||||
await ReplyErrorLocalized("something_went_wrong").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,41 +1,79 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using NadekoBot.Attributes;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using Discord.Commands;
|
||||
using NadekoBot.Extensions;
|
||||
|
||||
namespace NadekoBot.Modules.Searches
|
||||
{
|
||||
public partial class Searches
|
||||
{
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task Memelist()
|
||||
[Group]
|
||||
public class MemegenCommands : NadekoSubmodule
|
||||
{
|
||||
HttpClientHandler handler = new HttpClientHandler();
|
||||
|
||||
handler.AllowAutoRedirect = false;
|
||||
|
||||
using (var http = new HttpClient(handler))
|
||||
private static readonly ImmutableDictionary<char, string> _map = new Dictionary<char, string>()
|
||||
{
|
||||
var rawJson = await http.GetStringAsync("https://memegen.link/api/templates/").ConfigureAwait(false);
|
||||
var data = JsonConvert.DeserializeObject<Dictionary<string, string>>(rawJson)
|
||||
.Select(kvp => Path.GetFileName(kvp.Value));
|
||||
{'?', "~q"},
|
||||
{'%', "~p"},
|
||||
{'#', "~h"},
|
||||
{'/', "~s"},
|
||||
{' ', "-"},
|
||||
{'-', "--"},
|
||||
{'_', "__"},
|
||||
{'"', "''"}
|
||||
|
||||
await Context.Channel.SendTableAsync(data, x => $"{x,-17}", 3).ConfigureAwait(false);
|
||||
}.ToImmutableDictionary();
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task Memelist()
|
||||
{
|
||||
var handler = new HttpClientHandler
|
||||
{
|
||||
AllowAutoRedirect = false
|
||||
};
|
||||
|
||||
|
||||
using (var http = new HttpClient(handler))
|
||||
{
|
||||
var rawJson = await http.GetStringAsync("https://memegen.link/api/templates/").ConfigureAwait(false);
|
||||
var data = JsonConvert.DeserializeObject<Dictionary<string, string>>(rawJson)
|
||||
.Select(kvp => Path.GetFileName(kvp.Value));
|
||||
|
||||
await Context.Channel.SendTableAsync(data, x => $"{x,-17}", 3).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task Memegen(string meme, string topText, string botText)
|
||||
{
|
||||
var top = Replace(topText);
|
||||
var bot = Replace(botText);
|
||||
await Context.Channel.SendMessageAsync($"http://memegen.link/{meme}/{top}/{bot}.jpg")
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private static string Replace(string input)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
foreach (var c in input)
|
||||
{
|
||||
string tmp;
|
||||
if (_map.TryGetValue(c, out tmp))
|
||||
sb.Append(tmp);
|
||||
else
|
||||
sb.Append(c);
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task Memegen(string meme, string topText, string botText)
|
||||
{
|
||||
var top = Uri.EscapeDataString(topText.Replace(' ', '-'));
|
||||
var bot = Uri.EscapeDataString(botText.Replace(' ', '-'));
|
||||
await Context.Channel.SendMessageAsync($"http://memegen.link/{meme}/{top}/{bot}.jpg")
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -33,22 +33,23 @@ namespace NadekoBot.Modules.Searches.Models
|
||||
public string[] Evos { get; set; }
|
||||
public string[] EggGroups { get; set; }
|
||||
|
||||
public override string ToString() => $@"`Name:` {Species}
|
||||
`Types:` {string.Join(", ", Types)}
|
||||
`Stats:` {BaseStats}
|
||||
`Height:` {HeightM,4}m `Weight:` {WeightKg}kg
|
||||
`Abilities:` {string.Join(", ", Abilities.Values)}";
|
||||
// public override string ToString() => $@"`Name:` {Species}
|
||||
//`Types:` {string.Join(", ", Types)}
|
||||
//`Stats:` {BaseStats}
|
||||
//`Height:` {HeightM,4}m `Weight:` {WeightKg}kg
|
||||
//`Abilities:` {string.Join(", ", Abilities.Values)}";
|
||||
|
||||
}
|
||||
|
||||
public class SearchPokemonAbility
|
||||
{
|
||||
public string Desc { get; set; }
|
||||
public string ShortDesc { get; set; }
|
||||
public string Name { get; set; }
|
||||
public float Rating { get; set; }
|
||||
|
||||
public override string ToString() => $@"`Name:` : {Name}
|
||||
`Rating:` {Rating}
|
||||
`Description:` {Desc}";
|
||||
// public override string ToString() => $@"`Name:` : {Name}
|
||||
//`Rating:` {Rating}
|
||||
//`Description:` {Desc}";
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ using Discord.Commands;
|
||||
using NadekoBot.Attributes;
|
||||
using NadekoBot.Extensions;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
@ -16,21 +15,15 @@ namespace NadekoBot.Modules.Searches
|
||||
public partial class Searches
|
||||
{
|
||||
[Group]
|
||||
public class OsuCommands : ModuleBase
|
||||
public class OsuCommands : NadekoSubmodule
|
||||
{
|
||||
private static Logger _log { get; }
|
||||
|
||||
static OsuCommands()
|
||||
{
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
}
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task Osu(string usr, [Remainder] string mode = null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(usr))
|
||||
return;
|
||||
|
||||
using (HttpClient http = new HttpClient())
|
||||
using (var http = new HttpClient())
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -42,15 +35,15 @@ namespace NadekoBot.Modules.Searches
|
||||
http.AddFakeHeaders();
|
||||
var res = await http.GetStreamAsync(new Uri($"http://lemmmy.pw/osusig/sig.php?uname={ usr }&flagshadow&xpbar&xpbarhex&pp=2&mode={m}")).ConfigureAwait(false);
|
||||
|
||||
MemoryStream ms = new MemoryStream();
|
||||
var ms = new MemoryStream();
|
||||
res.CopyTo(ms);
|
||||
ms.Position = 0;
|
||||
await Context.Channel.SendFileAsync(ms, $"{usr}.png", $"🎧 **Profile Link:** <https://new.ppy.sh/u/{Uri.EscapeDataString(usr)}>\n`Image provided by https://lemmmy.pw/osusig`").ConfigureAwait(false);
|
||||
await Context.Channel.SendFileAsync(ms, $"{usr}.png", $"🎧 **{GetText("profile_link")}** <https://new.ppy.sh/u/{Uri.EscapeDataString(usr)}>\n`Image provided by https://lemmmy.pw/osusig`").ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("Failed retrieving osu signature.").ConfigureAwait(false);
|
||||
_log.Warn(ex, "Osu command failed");
|
||||
await ReplyErrorLocalized("osu_failed").ConfigureAwait(false);
|
||||
_log.Warn(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -60,7 +53,7 @@ namespace NadekoBot.Modules.Searches
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.OsuApiKey))
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("An osu! API key is required.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("osu_api_key").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -75,8 +68,8 @@ namespace NadekoBot.Modules.Searches
|
||||
var reqString = $"https://osu.ppy.sh/api/get_beatmaps?k={NadekoBot.Credentials.OsuApiKey}&{mapId}";
|
||||
var obj = JArray.Parse(await http.GetStringAsync(reqString).ConfigureAwait(false))[0];
|
||||
var sb = new System.Text.StringBuilder();
|
||||
var starRating = Math.Round(Double.Parse($"{obj["difficultyrating"]}", CultureInfo.InvariantCulture), 2);
|
||||
var time = TimeSpan.FromSeconds(Double.Parse($"{obj["total_length"]}")).ToString(@"mm\:ss");
|
||||
var starRating = Math.Round(double.Parse($"{obj["difficultyrating"]}", CultureInfo.InvariantCulture), 2);
|
||||
var time = TimeSpan.FromSeconds(double.Parse($"{obj["total_length"]}")).ToString(@"mm\:ss");
|
||||
sb.AppendLine($"{obj["artist"]} - {obj["title"]}, mapped by {obj["creator"]}. https://osu.ppy.sh/s/{obj["beatmapset_id"]}");
|
||||
sb.AppendLine($"{starRating} stars, {obj["bpm"]} BPM | AR{obj["diff_approach"]}, CS{obj["diff_size"]}, OD{obj["diff_overall"]} | Length: {time}");
|
||||
await Context.Channel.SendMessageAsync(sb.ToString()).ConfigureAwait(false);
|
||||
@ -84,8 +77,8 @@ namespace NadekoBot.Modules.Searches
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("Something went wrong.");
|
||||
_log.Warn(ex, "Osub command failed");
|
||||
await ReplyErrorLocalized("something_went_wrong").ConfigureAwait(false);
|
||||
_log.Warn(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@ -121,54 +114,53 @@ namespace NadekoBot.Modules.Searches
|
||||
{
|
||||
var mapReqString = $"https://osu.ppy.sh/api/get_beatmaps?k={NadekoBot.Credentials.OsuApiKey}&b={item["beatmap_id"]}";
|
||||
var map = JArray.Parse(await http.GetStringAsync(mapReqString).ConfigureAwait(false))[0];
|
||||
var pp = Math.Round(Double.Parse($"{item["pp"]}", CultureInfo.InvariantCulture), 2);
|
||||
var pp = Math.Round(double.Parse($"{item["pp"]}", CultureInfo.InvariantCulture), 2);
|
||||
var acc = CalculateAcc(item, m);
|
||||
var mods = ResolveMods(Int32.Parse($"{item["enabled_mods"]}"));
|
||||
if (mods != "+")
|
||||
sb.AppendLine($"{pp + "pp",-7} | {acc + "%",-7} | {map["artist"] + "-" + map["title"] + " (" + map["version"] + ")",-40} | **{mods,-10}** | /b/{item["beatmap_id"]}");
|
||||
else
|
||||
sb.AppendLine($"{pp + "pp",-7} | {acc + "%",-7} | {map["artist"] + "-" + map["title"] + " (" + map["version"] + ")",-40} | /b/{item["beatmap_id"]}");
|
||||
var mods = ResolveMods(int.Parse($"{item["enabled_mods"]}"));
|
||||
sb.AppendLine(mods != "+"
|
||||
? $"{pp + "pp",-7} | {acc + "%",-7} | {map["artist"] + "-" + map["title"] + " (" + map["version"] + ")",-40} | **{mods,-10}** | /b/{item["beatmap_id"]}"
|
||||
: $"{pp + "pp",-7} | {acc + "%",-7} | {map["artist"] + "-" + map["title"] + " (" + map["version"] + ")",-40} | /b/{item["beatmap_id"]}");
|
||||
}
|
||||
sb.Append("```");
|
||||
await channel.SendMessageAsync(sb.ToString()).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await channel.SendErrorAsync("Something went wrong.");
|
||||
_log.Warn(ex, "Osu5 command failed");
|
||||
await ReplyErrorLocalized("something_went_wrong").ConfigureAwait(false);
|
||||
_log.Warn(ex);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//https://osu.ppy.sh/wiki/Accuracy
|
||||
private static Double CalculateAcc(JToken play, int mode)
|
||||
private static double CalculateAcc(JToken play, int mode)
|
||||
{
|
||||
if (mode == 0)
|
||||
{
|
||||
var hitPoints = Double.Parse($"{play["count50"]}") * 50 + Double.Parse($"{play["count100"]}") * 100 + Double.Parse($"{play["count300"]}") * 300;
|
||||
var totalHits = Double.Parse($"{play["count50"]}") + Double.Parse($"{play["count100"]}") + Double.Parse($"{play["count300"]}") + Double.Parse($"{play["countmiss"]}");
|
||||
var hitPoints = double.Parse($"{play["count50"]}") * 50 + double.Parse($"{play["count100"]}") * 100 + double.Parse($"{play["count300"]}") * 300;
|
||||
var totalHits = double.Parse($"{play["count50"]}") + double.Parse($"{play["count100"]}") + double.Parse($"{play["count300"]}") + double.Parse($"{play["countmiss"]}");
|
||||
totalHits *= 300;
|
||||
return Math.Round(hitPoints / totalHits * 100, 2);
|
||||
}
|
||||
else if (mode == 1)
|
||||
{
|
||||
var hitPoints = Double.Parse($"{play["countmiss"]}") * 0 + Double.Parse($"{play["count100"]}") * 0.5 + Double.Parse($"{play["count300"]}") * 1;
|
||||
var totalHits = Double.Parse($"{play["countmiss"]}") + Double.Parse($"{play["count100"]}") + Double.Parse($"{play["count300"]}");
|
||||
var hitPoints = double.Parse($"{play["countmiss"]}") * 0 + double.Parse($"{play["count100"]}") * 0.5 + double.Parse($"{play["count300"]}") * 1;
|
||||
var totalHits = double.Parse($"{play["countmiss"]}") + double.Parse($"{play["count100"]}") + double.Parse($"{play["count300"]}");
|
||||
hitPoints *= 300;
|
||||
totalHits *= 300;
|
||||
return Math.Round(hitPoints / totalHits * 100, 2);
|
||||
}
|
||||
else if (mode == 2)
|
||||
{
|
||||
var fruitsCaught = Double.Parse($"{play["count50"]}") + Double.Parse($"{play["count100"]}") + Double.Parse($"{play["count300"]}");
|
||||
var totalFruits = Double.Parse($"{play["countmiss"]}") + Double.Parse($"{play["count50"]}") + Double.Parse($"{play["count100"]}") + Double.Parse($"{play["count300"]}") + Double.Parse($"{play["countkatu"]}");
|
||||
var fruitsCaught = double.Parse($"{play["count50"]}") + double.Parse($"{play["count100"]}") + double.Parse($"{play["count300"]}");
|
||||
var totalFruits = double.Parse($"{play["countmiss"]}") + double.Parse($"{play["count50"]}") + double.Parse($"{play["count100"]}") + double.Parse($"{play["count300"]}") + double.Parse($"{play["countkatu"]}");
|
||||
return Math.Round(fruitsCaught / totalFruits * 100, 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
var hitPoints = Double.Parse($"{play["count50"]}") * 50 + Double.Parse($"{play["count100"]}") * 100 + Double.Parse($"{play["countkatu"]}") * 200 + (Double.Parse($"{play["count300"]}") + Double.Parse($"{play["countgeki"]}")) * 300;
|
||||
var totalHits = Double.Parse($"{play["countmiss"]}") + Double.Parse($"{play["count50"]}") + Double.Parse($"{play["count100"]}") + Double.Parse($"{play["countkatu"]}") + Double.Parse($"{play["count300"]}") + Double.Parse($"{play["countgeki"]}");
|
||||
var hitPoints = double.Parse($"{play["count50"]}") * 50 + double.Parse($"{play["count100"]}") * 100 + double.Parse($"{play["countkatu"]}") * 200 + (double.Parse($"{play["count300"]}") + double.Parse($"{play["countgeki"]}")) * 300;
|
||||
var totalHits = double.Parse($"{play["countmiss"]}") + double.Parse($"{play["count50"]}") + double.Parse($"{play["count100"]}") + double.Parse($"{play["countkatu"]}") + double.Parse($"{play["count300"]}") + double.Parse($"{play["countgeki"]}");
|
||||
totalHits *= 300;
|
||||
return Math.Round(hitPoints / totalHits * 100, 2);
|
||||
}
|
||||
@ -176,10 +168,10 @@ namespace NadekoBot.Modules.Searches
|
||||
|
||||
private static string ResolveMap(string mapLink)
|
||||
{
|
||||
Match s = new Regex(@"osu.ppy.sh\/s\/", RegexOptions.IgnoreCase).Match(mapLink);
|
||||
Match b = new Regex(@"osu.ppy.sh\/b\/", RegexOptions.IgnoreCase).Match(mapLink);
|
||||
Match p = new Regex(@"osu.ppy.sh\/p\/", RegexOptions.IgnoreCase).Match(mapLink);
|
||||
Match m = new Regex(@"&m=", RegexOptions.IgnoreCase).Match(mapLink);
|
||||
var s = new Regex(@"osu.ppy.sh\/s\/", RegexOptions.IgnoreCase).Match(mapLink);
|
||||
var b = new Regex(@"osu.ppy.sh\/b\/", RegexOptions.IgnoreCase).Match(mapLink);
|
||||
var p = new Regex(@"osu.ppy.sh\/p\/", RegexOptions.IgnoreCase).Match(mapLink);
|
||||
var m = new Regex(@"&m=", RegexOptions.IgnoreCase).Match(mapLink);
|
||||
if (s.Success)
|
||||
{
|
||||
var mapId = mapLink.Substring(mapLink.IndexOf("/s/") + 3);
|
||||
|
@ -4,7 +4,6 @@ using NadekoBot.Attributes;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Modules.Searches.Models;
|
||||
using Newtonsoft.Json;
|
||||
using NLog;
|
||||
using System.Net.Http;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
@ -14,13 +13,8 @@ namespace NadekoBot.Modules.Searches
|
||||
public partial class Searches
|
||||
{
|
||||
[Group]
|
||||
public class OverwatchCommands : ModuleBase
|
||||
public class OverwatchCommands : NadekoSubmodule
|
||||
{
|
||||
private readonly Logger _log;
|
||||
public OverwatchCommands()
|
||||
{
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
}
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task Overwatch(string region, [Remainder] string query = null)
|
||||
{
|
||||
@ -34,9 +28,9 @@ namespace NadekoBot.Modules.Searches
|
||||
await Context.Channel.TriggerTypingAsync().ConfigureAwait(false);
|
||||
var model = await GetProfile(region, battletag);
|
||||
|
||||
var rankimg = $"{model.Competitive.rank_img}";
|
||||
var rank = $"{model.Competitive.rank}";
|
||||
var competitiveplay = $"{model.Games.Competitive.played}";
|
||||
var rankimg = model.Competitive.rank_img;
|
||||
var rank = model.Competitive.rank;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(rank))
|
||||
{
|
||||
var embed = new EmbedBuilder()
|
||||
@ -44,10 +38,10 @@ namespace NadekoBot.Modules.Searches
|
||||
.WithUrl($"https://www.overbuff.com/players/pc/{battletag}")
|
||||
.WithIconUrl($"{model.avatar}"))
|
||||
.WithThumbnailUrl("https://cdn.discordapp.com/attachments/155726317222887425/255653487512256512/YZ4w2ey.png")
|
||||
.AddField(fb => fb.WithName("**Level**").WithValue($"{model.level}").WithIsInline(true))
|
||||
.AddField(fb => fb.WithName("**Quick Wins**").WithValue($"{model.Games.Quick.wins}").WithIsInline(true))
|
||||
.AddField(fb => fb.WithName("**Competitive Rank**").WithValue("0").WithIsInline(true))
|
||||
.AddField(fb => fb.WithName("**Quick Playtime**").WithValue($"{model.Playtime.quick}").WithIsInline(true))
|
||||
.AddField(fb => fb.WithName(GetText("level")).WithValue($"{model.level}").WithIsInline(true))
|
||||
.AddField(fb => fb.WithName(GetText("quick_wins")).WithValue($"{model.Games.Quick.wins}").WithIsInline(true))
|
||||
.AddField(fb => fb.WithName(GetText("compet_rank")).WithValue("0").WithIsInline(true))
|
||||
.AddField(fb => fb.WithName(GetText("quick_playtime")).WithValue($"{model.Playtime.quick}").WithIsInline(true))
|
||||
.WithOkColor();
|
||||
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
|
||||
}
|
||||
@ -58,22 +52,21 @@ namespace NadekoBot.Modules.Searches
|
||||
.WithUrl($"https://www.overbuff.com/players/pc/{battletag}")
|
||||
.WithIconUrl($"{model.avatar}"))
|
||||
.WithThumbnailUrl(rankimg)
|
||||
.AddField(fb => fb.WithName("**Level**").WithValue($"{model.level}").WithIsInline(true))
|
||||
.AddField(fb => fb.WithName("**Quick Wins**").WithValue($"{model.Games.Quick.wins}").WithIsInline(true))
|
||||
.AddField(fb => fb.WithName("**Competitive Wins**").WithValue($"{model.Games.Competitive.wins}").WithIsInline(true))
|
||||
.AddField(fb => fb.WithName("**Competitive Loses**").WithValue($"{model.Games.Competitive.lost}").WithIsInline(true))
|
||||
.AddField(fb => fb.WithName("**Competitive Played**").WithValue($"{model.Games.Competitive.played}").WithIsInline(true))
|
||||
.AddField(fb => fb.WithName("**Competitive Rank**").WithValue(rank).WithIsInline(true))
|
||||
.AddField(fb => fb.WithName("**Competitive Playtime**").WithValue($"{model.Playtime.competitive}").WithIsInline(true))
|
||||
.AddField(fb => fb.WithName("**Quick Playtime**").WithValue($"{model.Playtime.quick}").WithIsInline(true))
|
||||
.AddField(fb => fb.WithName(GetText("level")).WithValue($"{model.level}").WithIsInline(true))
|
||||
.AddField(fb => fb.WithName(GetText("quick_wins")).WithValue($"{model.Games.Quick.wins}").WithIsInline(true))
|
||||
.AddField(fb => fb.WithName(GetText("compet_wins")).WithValue($"{model.Games.Competitive.wins}").WithIsInline(true))
|
||||
.AddField(fb => fb.WithName(GetText("compet_losses")).WithValue($"{model.Games.Competitive.lost}").WithIsInline(true))
|
||||
.AddField(fb => fb.WithName(GetText("compet_played")).WithValue($"{model.Games.Competitive.played}").WithIsInline(true))
|
||||
.AddField(fb => fb.WithName(GetText("compet_rank")).WithValue(rank).WithIsInline(true))
|
||||
.AddField(fb => fb.WithName(GetText("compet_played")).WithValue($"{model.Playtime.competitive}").WithIsInline(true))
|
||||
.AddField(fb => fb.WithName(GetText("quick_playtime")).WithValue($"{model.Playtime.quick}").WithIsInline(true))
|
||||
.WithColor(NadekoBot.OkColor);
|
||||
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("Found no user! Please check the **Region** and **BattleTag** before trying again.");
|
||||
await ReplyErrorLocalized("ow_user_not_found").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
public async Task<OverwatchApiModel.OverwatchPlayer.Data> GetProfile(string region, string battletag)
|
||||
@ -82,8 +75,8 @@ namespace NadekoBot.Modules.Searches
|
||||
{
|
||||
using (var http = new HttpClient())
|
||||
{
|
||||
var Url = await http.GetStringAsync($"https://api.lootbox.eu/pc/{region.ToLower()}/{battletag}/profile");
|
||||
var model = JsonConvert.DeserializeObject<OverwatchApiModel.OverwatchPlayer>(Url);
|
||||
var url = await http.GetStringAsync($"https://api.lootbox.eu/pc/{region.ToLower()}/{battletag}/profile");
|
||||
var model = JsonConvert.DeserializeObject<OverwatchApiModel.OverwatchPlayer>(url);
|
||||
return model.data;
|
||||
}
|
||||
}
|
||||
|
@ -10,9 +10,9 @@ namespace NadekoBot.Modules.Searches
|
||||
public partial class Searches
|
||||
{
|
||||
[Group]
|
||||
public class PlaceCommands : ModuleBase
|
||||
public class PlaceCommands : NadekoSubmodule
|
||||
{
|
||||
private static string typesStr { get; } = $"`List of \"{NadekoBot.ModulePrefixes[typeof(Searches).Name]}place\" tags:`\n" + String.Join(", ", Enum.GetNames(typeof(PlaceType)));
|
||||
private static string typesStr { get; } = string.Join(", ", Enum.GetNames(typeof(PlaceType)));
|
||||
|
||||
public enum PlaceType
|
||||
{
|
||||
@ -29,14 +29,15 @@ namespace NadekoBot.Modules.Searches
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task Placelist()
|
||||
{
|
||||
await Context.Channel.SendConfirmAsync(typesStr)
|
||||
await Context.Channel.SendConfirmAsync(GetText("list_of_place_tags", NadekoBot.ModulePrefixes[typeof(Searches).Name]),
|
||||
typesStr)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task Place(PlaceType placeType, uint width = 0, uint height = 0)
|
||||
{
|
||||
string url = "";
|
||||
var url = "";
|
||||
switch (placeType)
|
||||
{
|
||||
case PlaceType.Cage:
|
||||
|
@ -6,7 +6,6 @@ using NadekoBot.Modules.Searches.Models;
|
||||
using Newtonsoft.Json;
|
||||
using NLog;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
@ -16,7 +15,7 @@ namespace NadekoBot.Modules.Searches
|
||||
public partial class Searches
|
||||
{
|
||||
[Group]
|
||||
public class PokemonSearchCommands : ModuleBase
|
||||
public class PokemonSearchCommands : NadekoSubmodule
|
||||
{
|
||||
private static Dictionary<string, SearchPokemon> pokemons { get; } = new Dictionary<string, SearchPokemon>();
|
||||
private static Dictionary<string, SearchPokemonAbility> pokemonAbilities { get; } = new Dictionary<string, SearchPokemonAbility>();
|
||||
@ -24,7 +23,7 @@ namespace NadekoBot.Modules.Searches
|
||||
public const string PokemonAbilitiesFile = "data/pokemon/pokemon_abilities7.json";
|
||||
|
||||
public const string PokemonListFile = "data/pokemon/pokemon_list7.json";
|
||||
private static Logger _log { get; }
|
||||
private new static readonly Logger _log;
|
||||
|
||||
static PokemonSearchCommands()
|
||||
{
|
||||
@ -57,14 +56,13 @@ namespace NadekoBot.Modules.Searches
|
||||
await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||
.WithTitle(kvp.Key.ToTitleCase())
|
||||
.WithDescription(p.BaseStats.ToString())
|
||||
.AddField(efb => efb.WithName("Types").WithValue(string.Join(",\n", p.Types)).WithIsInline(true))
|
||||
.AddField(efb => efb.WithName("Height/Weight").WithValue($"{p.HeightM}m/{p.WeightKg}kg").WithIsInline(true))
|
||||
.AddField(efb => efb.WithName("Abilitities").WithValue(string.Join(",\n", p.Abilities.Select(a => a.Value))).WithIsInline(true))
|
||||
);
|
||||
.AddField(efb => efb.WithName(GetText("types")).WithValue(string.Join(",\n", p.Types)).WithIsInline(true))
|
||||
.AddField(efb => efb.WithName(GetText("height_weight")).WithValue(GetText("height_weight_val", p.HeightM, p.WeightKg)).WithIsInline(true))
|
||||
.AddField(efb => efb.WithName(GetText("abilities")).WithValue(string.Join(",\n", p.Abilities.Select(a => a.Value))).WithIsInline(true)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
await Context.Channel.SendErrorAsync("No pokemon found.");
|
||||
await ReplyErrorLocalized("pokemon_none").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -79,13 +77,16 @@ namespace NadekoBot.Modules.Searches
|
||||
{
|
||||
await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||
.WithTitle(kvp.Value.Name)
|
||||
.WithDescription(kvp.Value.Desc)
|
||||
.AddField(efb => efb.WithName("Rating").WithValue(kvp.Value.Rating.ToString()).WithIsInline(true))
|
||||
.WithDescription(string.IsNullOrWhiteSpace(kvp.Value.Desc)
|
||||
? kvp.Value.ShortDesc
|
||||
: kvp.Value.Desc)
|
||||
.AddField(efb => efb.WithName(GetText("rating"))
|
||||
.WithValue(kvp.Value.Rating.ToString(_cultureInfo)).WithIsInline(true))
|
||||
).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
await Context.Channel.SendErrorAsync("No ability found.");
|
||||
await ReplyErrorLocalized("pokemon_ability_none").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,9 +12,7 @@ using System.Net.Http;
|
||||
using NadekoBot.Attributes;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Newtonsoft.Json;
|
||||
using NLog;
|
||||
using NadekoBot.Extensions;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace NadekoBot.Modules.Searches
|
||||
{
|
||||
@ -65,23 +63,19 @@ namespace NadekoBot.Modules.Searches
|
||||
}
|
||||
|
||||
[Group]
|
||||
public class StreamNotificationCommands : ModuleBase
|
||||
public class StreamNotificationCommands : NadekoSubmodule
|
||||
{
|
||||
private static Timer checkTimer { get; }
|
||||
private static ConcurrentDictionary<string, StreamStatus> oldCachedStatuses = new ConcurrentDictionary<string, StreamStatus>();
|
||||
private static ConcurrentDictionary<string, StreamStatus> cachedStatuses = new ConcurrentDictionary<string, StreamStatus>();
|
||||
private static Logger _log { get; }
|
||||
private static readonly Timer _checkTimer;
|
||||
private static readonly ConcurrentDictionary<string, StreamStatus> _cachedStatuses = new ConcurrentDictionary<string, StreamStatus>();
|
||||
|
||||
private static bool FirstPass { get; set; } = true;
|
||||
private static bool firstPass { get; set; } = true;
|
||||
|
||||
static StreamNotificationCommands()
|
||||
{
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
checkTimer = new Timer(async (state) =>
|
||||
_checkTimer = new Timer(async (state) =>
|
||||
{
|
||||
oldCachedStatuses = new ConcurrentDictionary<string, StreamStatus>(cachedStatuses);
|
||||
cachedStatuses.Clear();
|
||||
var oldCachedStatuses = new ConcurrentDictionary<string, StreamStatus>(_cachedStatuses);
|
||||
_cachedStatuses.Clear();
|
||||
IEnumerable<FollowedStream> streams;
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
{
|
||||
@ -93,7 +87,7 @@ namespace NadekoBot.Modules.Searches
|
||||
try
|
||||
{
|
||||
var newStatus = await GetStreamStatus(fs).ConfigureAwait(false);
|
||||
if (FirstPass)
|
||||
if (firstPass)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@ -103,22 +97,26 @@ namespace NadekoBot.Modules.Searches
|
||||
oldStatus.IsLive != newStatus.IsLive)
|
||||
{
|
||||
var server = NadekoBot.Client.GetGuild(fs.GuildId);
|
||||
if (server == null)
|
||||
return;
|
||||
var channel = server.GetTextChannel(fs.ChannelId);
|
||||
var channel = server?.GetTextChannel(fs.ChannelId);
|
||||
if (channel == null)
|
||||
return;
|
||||
try
|
||||
{
|
||||
var msg = await channel.EmbedAsync(fs.GetEmbed(newStatus)).ConfigureAwait(false);
|
||||
await channel.EmbedAsync(fs.GetEmbed(newStatus, channel.Guild.Id)).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}));
|
||||
|
||||
FirstPass = false;
|
||||
firstPass = false;
|
||||
}, null, TimeSpan.Zero, TimeSpan.FromSeconds(60));
|
||||
}
|
||||
|
||||
@ -130,7 +128,7 @@ namespace NadekoBot.Modules.Searches
|
||||
{
|
||||
case FollowedStream.FollowedStreamType.Hitbox:
|
||||
var hitboxUrl = $"https://api.hitbox.tv/media/status/{stream.Username.ToLowerInvariant()}";
|
||||
if (checkCache && cachedStatuses.TryGetValue(hitboxUrl, out result))
|
||||
if (checkCache && _cachedStatuses.TryGetValue(hitboxUrl, out result))
|
||||
return result;
|
||||
using (var http = new HttpClient())
|
||||
{
|
||||
@ -145,11 +143,11 @@ namespace NadekoBot.Modules.Searches
|
||||
ApiLink = hitboxUrl,
|
||||
Views = hbData.Views
|
||||
};
|
||||
cachedStatuses.AddOrUpdate(hitboxUrl, result, (key, old) => result);
|
||||
_cachedStatuses.AddOrUpdate(hitboxUrl, result, (key, old) => result);
|
||||
return result;
|
||||
case FollowedStream.FollowedStreamType.Twitch:
|
||||
var twitchUrl = $"https://api.twitch.tv/kraken/streams/{Uri.EscapeUriString(stream.Username.ToLowerInvariant())}?client_id=67w6z9i09xv2uoojdm9l0wsyph4hxo6";
|
||||
if (checkCache && cachedStatuses.TryGetValue(twitchUrl, out result))
|
||||
if (checkCache && _cachedStatuses.TryGetValue(twitchUrl, out result))
|
||||
return result;
|
||||
using (var http = new HttpClient())
|
||||
{
|
||||
@ -166,11 +164,11 @@ namespace NadekoBot.Modules.Searches
|
||||
ApiLink = twitchUrl,
|
||||
Views = twData.Stream?.Viewers.ToString() ?? "0"
|
||||
};
|
||||
cachedStatuses.AddOrUpdate(twitchUrl, result, (key, old) => result);
|
||||
_cachedStatuses.AddOrUpdate(twitchUrl, result, (key, old) => result);
|
||||
return result;
|
||||
case FollowedStream.FollowedStreamType.Beam:
|
||||
var beamUrl = $"https://beam.pro/api/v1/channels/{stream.Username.ToLowerInvariant()}";
|
||||
if (checkCache && cachedStatuses.TryGetValue(beamUrl, out result))
|
||||
if (checkCache && _cachedStatuses.TryGetValue(beamUrl, out result))
|
||||
return result;
|
||||
using (var http = new HttpClient())
|
||||
{
|
||||
@ -186,7 +184,7 @@ namespace NadekoBot.Modules.Searches
|
||||
ApiLink = beamUrl,
|
||||
Views = bmData.ViewersCurrent.ToString()
|
||||
};
|
||||
cachedStatuses.AddOrUpdate(beamUrl, result, (key, old) => result);
|
||||
_cachedStatuses.AddOrUpdate(beamUrl, result, (key, old) => result);
|
||||
return result;
|
||||
default:
|
||||
break;
|
||||
@ -230,17 +228,21 @@ namespace NadekoBot.Modules.Searches
|
||||
|
||||
if (!streams.Any())
|
||||
{
|
||||
await Context.Channel.SendConfirmAsync("You are not following any streams on this server.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("streams_none").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
var text = string.Join("\n", await Task.WhenAll(streams.Select(async snc =>
|
||||
{
|
||||
var ch = await Context.Guild.GetTextChannelAsync(snc.ChannelId);
|
||||
return $"`{snc.Username}`'s stream on **{(ch)?.Name}** channel. 【`{snc.Type.ToString()}`】";
|
||||
return string.Format("{0}'s stream on {1} channel. 【{2}】",
|
||||
Format.Code(snc.Username),
|
||||
Format.Bold(ch?.Name ?? "deleted-channel"),
|
||||
Format.Code(snc.Type.ToString()));
|
||||
})));
|
||||
|
||||
await Context.Channel.SendConfirmAsync($"You are following **{streams.Count()}** streams on this server.\n\n" + text).ConfigureAwait(false);
|
||||
|
||||
await Context.Channel.SendConfirmAsync(GetText("streams_following", streams.Count()) + "\n\n" + text)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -267,10 +269,13 @@ namespace NadekoBot.Modules.Searches
|
||||
}
|
||||
if (!removed)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("No such stream.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("stream_no").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
await Context.Channel.SendConfirmAsync($"Removed `{username}`'s stream ({type}) from notifications.").ConfigureAwait(false);
|
||||
|
||||
await ReplyConfirmLocalized("stream_removed",
|
||||
Format.Code(username),
|
||||
type).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -289,20 +294,24 @@ namespace NadekoBot.Modules.Searches
|
||||
}));
|
||||
if (streamStatus.IsLive)
|
||||
{
|
||||
await Context.Channel.SendConfirmAsync($"Streamer {username} is online with {streamStatus.Views} viewers.");
|
||||
await ReplyConfirmLocalized("streamer_online",
|
||||
username,
|
||||
streamStatus.Views)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await Context.Channel.SendConfirmAsync($"Streamer {username} is offline.");
|
||||
await ReplyConfirmLocalized("streamer_offline",
|
||||
username).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("No channel found.");
|
||||
await ReplyErrorLocalized("no_channel_found").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task TrackStream(ITextChannel channel, string username, FollowedStream.FollowedStreamType type)
|
||||
private async Task TrackStream(ITextChannel channel, string username, FollowedStream.FollowedStreamType type)
|
||||
{
|
||||
username = username.ToLowerInvariant().Trim();
|
||||
var fs = new FollowedStream
|
||||
@ -320,7 +329,7 @@ namespace NadekoBot.Modules.Searches
|
||||
}
|
||||
catch
|
||||
{
|
||||
await channel.SendErrorAsync("Stream probably doesn't exist.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("stream_not_exist").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -331,24 +340,24 @@ namespace NadekoBot.Modules.Searches
|
||||
.Add(fs);
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
await channel.EmbedAsync(fs.GetEmbed(status), $"🆗 I will notify this channel when status changes.").ConfigureAwait(false);
|
||||
await channel.EmbedAsync(fs.GetEmbed(status, Context.Guild.Id), GetText("stream_tracked")).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class FollowedStreamExtensions
|
||||
{
|
||||
public static EmbedBuilder GetEmbed(this FollowedStream fs, Searches.StreamStatus status)
|
||||
public static EmbedBuilder GetEmbed(this FollowedStream fs, Searches.StreamStatus status, ulong guildId)
|
||||
{
|
||||
var embed = new EmbedBuilder().WithTitle(fs.Username)
|
||||
.WithUrl(fs.GetLink())
|
||||
.AddField(efb => efb.WithName("Status")
|
||||
.AddField(efb => efb.WithName(fs.GetText("status"))
|
||||
.WithValue(status.IsLive ? "Online" : "Offline")
|
||||
.WithIsInline(true))
|
||||
.AddField(efb => efb.WithName("Viewers")
|
||||
.AddField(efb => efb.WithName(fs.GetText("viewers"))
|
||||
.WithValue(status.IsLive ? status.Views : "-")
|
||||
.WithIsInline(true))
|
||||
.AddField(efb => efb.WithName("Platform")
|
||||
.AddField(efb => efb.WithName(fs.GetText("platform"))
|
||||
.WithValue(fs.Type.ToString())
|
||||
.WithIsInline(true))
|
||||
.WithColor(status.IsLive ? NadekoBot.OkColor : NadekoBot.ErrorColor);
|
||||
@ -356,15 +365,21 @@ namespace NadekoBot.Modules.Searches
|
||||
return embed;
|
||||
}
|
||||
|
||||
public static string GetLink(this FollowedStream fs) {
|
||||
public static string GetText(this FollowedStream fs, string key, params object[] replacements) =>
|
||||
NadekoTopLevelModule.GetTextStatic(key,
|
||||
NadekoBot.Localization.GetCultureInfo(fs.GuildId),
|
||||
typeof(Searches).Name.ToLowerInvariant(),
|
||||
replacements);
|
||||
|
||||
public static string GetLink(this FollowedStream fs)
|
||||
{
|
||||
if (fs.Type == FollowedStream.FollowedStreamType.Hitbox)
|
||||
return $"http://www.hitbox.tv/{fs.Username}/";
|
||||
else if (fs.Type == FollowedStream.FollowedStreamType.Twitch)
|
||||
if (fs.Type == FollowedStream.FollowedStreamType.Twitch)
|
||||
return $"http://www.twitch.tv/{fs.Username}/";
|
||||
else if (fs.Type == FollowedStream.FollowedStreamType.Beam)
|
||||
if (fs.Type == FollowedStream.FollowedStreamType.Beam)
|
||||
return $"https://beam.pro/{fs.Username}/";
|
||||
else
|
||||
return "??";
|
||||
return "??";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,10 +19,10 @@ namespace NadekoBot.Modules.Searches
|
||||
}
|
||||
|
||||
[Group]
|
||||
public class TranslateCommands : ModuleBase
|
||||
public class TranslateCommands : NadekoSubmodule
|
||||
{
|
||||
private static ConcurrentDictionary<ulong, bool> TranslatedChannels { get; } = new ConcurrentDictionary<ulong, bool>();
|
||||
private static ConcurrentDictionary<UserChannelPair, string> UserLanguages { get; } = new ConcurrentDictionary<UserChannelPair, string>();
|
||||
private static ConcurrentDictionary<ulong, bool> translatedChannels { get; } = new ConcurrentDictionary<ulong, bool>();
|
||||
private static ConcurrentDictionary<UserChannelPair, string> userLanguages { get; } = new ConcurrentDictionary<UserChannelPair, string>();
|
||||
|
||||
static TranslateCommands()
|
||||
{
|
||||
@ -35,7 +35,7 @@ namespace NadekoBot.Modules.Searches
|
||||
return;
|
||||
|
||||
bool autoDelete;
|
||||
if (!TranslatedChannels.TryGetValue(umsg.Channel.Id, out autoDelete))
|
||||
if (!translatedChannels.TryGetValue(umsg.Channel.Id, out autoDelete))
|
||||
return;
|
||||
var key = new UserChannelPair()
|
||||
{
|
||||
@ -44,10 +44,10 @@ namespace NadekoBot.Modules.Searches
|
||||
};
|
||||
|
||||
string langs;
|
||||
if (!UserLanguages.TryGetValue(key, out langs))
|
||||
if (!userLanguages.TryGetValue(key, out langs))
|
||||
return;
|
||||
|
||||
var text = await TranslateInternal(langs, umsg.Resolve(TagHandling.Ignore), true)
|
||||
var text = await TranslateInternal(langs, umsg.Resolve(TagHandling.Ignore))
|
||||
.ConfigureAwait(false);
|
||||
if (autoDelete)
|
||||
try { await umsg.DeleteAsync().ConfigureAwait(false); } catch { }
|
||||
@ -64,21 +64,21 @@ namespace NadekoBot.Modules.Searches
|
||||
{
|
||||
await Context.Channel.TriggerTypingAsync().ConfigureAwait(false);
|
||||
var translation = await TranslateInternal(langs, text);
|
||||
await Context.Channel.SendConfirmAsync("Translation " + langs, translation).ConfigureAwait(false);
|
||||
await Context.Channel.SendConfirmAsync(GetText("translation") + " " + langs, translation).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("Bad input format, or something went wrong...").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("bad_input_format").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<string> TranslateInternal(string langs, [Remainder] string text = null, bool silent = false)
|
||||
private static async Task<string> TranslateInternal(string langs, [Remainder] string text = null)
|
||||
{
|
||||
var langarr = langs.ToLowerInvariant().Split('>');
|
||||
if (langarr.Length != 2)
|
||||
throw new ArgumentException();
|
||||
string from = langarr[0];
|
||||
string to = langarr[1];
|
||||
var from = langarr[0];
|
||||
var to = langarr[1];
|
||||
text = text?.Trim();
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
throw new ArgumentException();
|
||||
@ -101,20 +101,20 @@ namespace NadekoBot.Modules.Searches
|
||||
|
||||
if (autoDelete == AutoDeleteAutoTranslate.Del)
|
||||
{
|
||||
TranslatedChannels.AddOrUpdate(channel.Id, true, (key, val) => true);
|
||||
try { await channel.SendConfirmAsync("Started automatic translation of messages on this channel. User messages will be auto-deleted.").ConfigureAwait(false); } catch { }
|
||||
translatedChannels.AddOrUpdate(channel.Id, true, (key, val) => true);
|
||||
await ReplyConfirmLocalized("atl_ad_started").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
bool throwaway;
|
||||
if (TranslatedChannels.TryRemove(channel.Id, out throwaway))
|
||||
if (translatedChannels.TryRemove(channel.Id, out throwaway))
|
||||
{
|
||||
try { await channel.SendConfirmAsync("Stopped automatic translation of messages on this channel.").ConfigureAwait(false); } catch { }
|
||||
await ReplyConfirmLocalized("atl_stopped").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
else if (TranslatedChannels.TryAdd(channel.Id, autoDelete == AutoDeleteAutoTranslate.Del))
|
||||
if (translatedChannels.TryAdd(channel.Id, autoDelete == AutoDeleteAutoTranslate.Del))
|
||||
{
|
||||
try { await channel.SendConfirmAsync("Started automatic translation of messages on this channel.").ConfigureAwait(false); } catch { }
|
||||
await ReplyConfirmLocalized("atl_started").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -130,8 +130,8 @@ namespace NadekoBot.Modules.Searches
|
||||
|
||||
if (string.IsNullOrWhiteSpace(langs))
|
||||
{
|
||||
if (UserLanguages.TryRemove(ucp, out langs))
|
||||
await Context.Channel.SendConfirmAsync($"{Context.User.Mention}'s auto-translate language has been removed.").ConfigureAwait(false);
|
||||
if (userLanguages.TryRemove(ucp, out langs))
|
||||
await ReplyConfirmLocalized("atl_removed").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -143,20 +143,20 @@ namespace NadekoBot.Modules.Searches
|
||||
|
||||
if (!GoogleTranslator.Instance.Languages.Contains(from) || !GoogleTranslator.Instance.Languages.Contains(to))
|
||||
{
|
||||
try { await Context.Channel.SendErrorAsync("Invalid source and/or target language.").ConfigureAwait(false); } catch { }
|
||||
await ReplyErrorLocalized("invalid_lang").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
UserLanguages.AddOrUpdate(ucp, langs, (key, val) => langs);
|
||||
userLanguages.AddOrUpdate(ucp, langs, (key, val) => langs);
|
||||
|
||||
await Context.Channel.SendConfirmAsync($"Your auto-translate language has been set to {from}>{to}").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("atl_set", from, to).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task Translangs()
|
||||
{
|
||||
await Context.Channel.SendTableAsync(GoogleTranslator.Instance.Languages, str => $"{str,-15}", columns: 3);
|
||||
await Context.Channel.SendTableAsync(GoogleTranslator.Instance.Languages, str => $"{str,-15}", 3);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -12,9 +12,9 @@ namespace NadekoBot.Modules.Searches
|
||||
public partial class Searches
|
||||
{
|
||||
[Group]
|
||||
public class XkcdCommands : ModuleBase
|
||||
public class XkcdCommands : NadekoSubmodule
|
||||
{
|
||||
private const string xkcdUrl = "https://xkcd.com";
|
||||
private const string _xkcdUrl = "https://xkcd.com";
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[Priority(1)]
|
||||
@ -24,9 +24,9 @@ namespace NadekoBot.Modules.Searches
|
||||
{
|
||||
using (var http = new HttpClient())
|
||||
{
|
||||
var res = await http.GetStringAsync($"{xkcdUrl}/info.0.json").ConfigureAwait(false);
|
||||
var res = await http.GetStringAsync($"{_xkcdUrl}/info.0.json").ConfigureAwait(false);
|
||||
var comic = JsonConvert.DeserializeObject<XkcdComic>(res);
|
||||
var sent = await Context.Channel.SendMessageAsync($"{Context.User.Mention} " + comic.ToString())
|
||||
var sent = await Context.Channel.SendMessageAsync($"{Context.User.Mention} " + comic)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
await Task.Delay(10000).ConfigureAwait(false);
|
||||
@ -47,14 +47,14 @@ namespace NadekoBot.Modules.Searches
|
||||
|
||||
using (var http = new HttpClient())
|
||||
{
|
||||
var res = await http.GetStringAsync($"{xkcdUrl}/{num}/info.0.json").ConfigureAwait(false);
|
||||
var res = await http.GetStringAsync($"{_xkcdUrl}/{num}/info.0.json").ConfigureAwait(false);
|
||||
|
||||
var comic = JsonConvert.DeserializeObject<XkcdComic>(res);
|
||||
var embed = new EmbedBuilder().WithColor(NadekoBot.OkColor)
|
||||
.WithImageUrl(comic.ImageLink)
|
||||
.WithAuthor(eab => eab.WithName(comic.Title).WithUrl($"{xkcdUrl}/{num}").WithIconUrl("http://xkcd.com/s/919f27.ico"))
|
||||
.AddField(efb => efb.WithName("Comic#").WithValue(comic.Num.ToString()).WithIsInline(true))
|
||||
.AddField(efb => efb.WithName("Date").WithValue($"{comic.Month}/{comic.Year}").WithIsInline(true));
|
||||
.WithAuthor(eab => eab.WithName(comic.Title).WithUrl($"{_xkcdUrl}/{num}").WithIconUrl("http://xkcd.com/s/919f27.ico"))
|
||||
.AddField(efb => efb.WithName(GetText("comic_number")).WithValue(comic.Num.ToString()).WithIsInline(true))
|
||||
.AddField(efb => efb.WithName(GetText("date")).WithValue($"{comic.Month}/{comic.Year}").WithIsInline(true));
|
||||
var sent = await Context.Channel.EmbedAsync(embed)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
@ -75,9 +75,6 @@ namespace NadekoBot.Modules.Searches
|
||||
[JsonProperty("img")]
|
||||
public string ImageLink { get; set; }
|
||||
public string Alt { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
=> $"`Comic:` #{Num} `Title:` {Title} `Date:` {Month}/{Year}\n{ImageLink}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
@ -8,27 +7,26 @@ using System.Text;
|
||||
using System.Net.Http;
|
||||
using NadekoBot.Services;
|
||||
using System.Threading.Tasks;
|
||||
using NadekoBot.Attributes;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Net;
|
||||
using NadekoBot.Modules.Searches.Models;
|
||||
using System.Collections.Generic;
|
||||
using ImageSharp;
|
||||
using NadekoBot.Extensions;
|
||||
using System.IO;
|
||||
using NadekoBot.Modules.Searches.Commands.OMDB;
|
||||
using NadekoBot.Modules.Searches.Commands.Models;
|
||||
using AngleSharp.Parser.Html;
|
||||
using AngleSharp;
|
||||
using AngleSharp.Dom.Html;
|
||||
using AngleSharp.Dom;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
using Configuration = AngleSharp.Configuration;
|
||||
using NadekoBot.Attributes;
|
||||
using Discord.Commands;
|
||||
using ImageSharp.Processing.Processors;
|
||||
|
||||
namespace NadekoBot.Modules.Searches
|
||||
{
|
||||
[NadekoModule("Searches", "~")]
|
||||
public partial class Searches : DiscordModule
|
||||
public partial class Searches : NadekoTopLevelModule
|
||||
{
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task Weather([Remainder] string query)
|
||||
@ -43,15 +41,15 @@ namespace NadekoBot.Modules.Searches
|
||||
var data = JsonConvert.DeserializeObject<WeatherData>(response);
|
||||
|
||||
var embed = new EmbedBuilder()
|
||||
.AddField(fb => fb.WithName("🌍 **Location**").WithValue(data.name + ", " + data.sys.country).WithIsInline(true))
|
||||
.AddField(fb => fb.WithName("📏 **Lat,Long**").WithValue($"{data.coord.lat}, {data.coord.lon}").WithIsInline(true))
|
||||
.AddField(fb => fb.WithName("☁ **Condition**").WithValue(String.Join(", ", data.weather.Select(w => w.main))).WithIsInline(true))
|
||||
.AddField(fb => fb.WithName("😓 **Humidity**").WithValue($"{data.main.humidity}%").WithIsInline(true))
|
||||
.AddField(fb => fb.WithName("💨 **Wind Speed**").WithValue(data.wind.speed + " km/h").WithIsInline(true))
|
||||
.AddField(fb => fb.WithName("🌡 **Temperature**").WithValue(data.main.temp + "°C").WithIsInline(true))
|
||||
.AddField(fb => fb.WithName("🔆 **Min - Max**").WithValue($"{data.main.temp_min}°C - {data.main.temp_max}°C").WithIsInline(true))
|
||||
.AddField(fb => fb.WithName("🌄 **Sunrise (utc)**").WithValue($"{data.sys.sunrise.ToUnixTimestamp():HH:mm}").WithIsInline(true))
|
||||
.AddField(fb => fb.WithName("🌇 **Sunset (utc)**").WithValue($"{data.sys.sunset.ToUnixTimestamp():HH:mm}").WithIsInline(true))
|
||||
.AddField(fb => fb.WithName("🌍 " + GetText("location")).WithValue(data.name + ", " + data.sys.country).WithIsInline(true))
|
||||
.AddField(fb => fb.WithName("📏 " + GetText("latlong")).WithValue($"{data.coord.lat}, {data.coord.lon}").WithIsInline(true))
|
||||
.AddField(fb => fb.WithName("☁ " + GetText("condition")).WithValue(string.Join(", ", data.weather.Select(w => w.main))).WithIsInline(true))
|
||||
.AddField(fb => fb.WithName("😓 " + GetText("humidity")).WithValue($"{data.main.humidity}%").WithIsInline(true))
|
||||
.AddField(fb => fb.WithName("💨 " + GetText("wind_speed")).WithValue(data.wind.speed + " km/h").WithIsInline(true))
|
||||
.AddField(fb => fb.WithName("🌡 " + GetText("temperature")).WithValue(data.main.temp + "°C").WithIsInline(true))
|
||||
.AddField(fb => fb.WithName("🔆 " + GetText("min_max")).WithValue($"{data.main.temp_min}°C - {data.main.temp_max}°C").WithIsInline(true))
|
||||
.AddField(fb => fb.WithName("🌄 " + GetText("sunrise")).WithValue($"{data.sys.sunrise.ToUnixTimestamp():HH:mm}").WithIsInline(true))
|
||||
.AddField(fb => fb.WithName("🌇 " + GetText("sunset")).WithValue($"{data.sys.sunset.ToUnixTimestamp():HH:mm}").WithIsInline(true))
|
||||
.WithOkColor()
|
||||
.WithFooter(efb => efb.WithText("Powered by http://openweathermap.org"));
|
||||
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
|
||||
@ -60,17 +58,15 @@ namespace NadekoBot.Modules.Searches
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task Youtube([Remainder] string query = null)
|
||||
{
|
||||
if (!(await ValidateQuery(Context.Channel, query).ConfigureAwait(false))) return;
|
||||
if (!await ValidateQuery(Context.Channel, query).ConfigureAwait(false)) return;
|
||||
var result = (await NadekoBot.Google.GetVideosByKeywordsAsync(query, 1)).FirstOrDefault();
|
||||
if (string.IsNullOrWhiteSpace(result))
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("No results found for that query.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("no_results").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
await Context.Channel.SendMessageAsync(result).ConfigureAwait(false);
|
||||
|
||||
//await Context.Channel.EmbedAsync(new Discord.API.Embed() { Video = new Discord.API.EmbedVideo() { Url = result.Replace("watch?v=", "embed/") }, Color = NadekoBot.OkColor }).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -82,7 +78,7 @@ namespace NadekoBot.Modules.Searches
|
||||
var movie = await OmdbProvider.FindMovie(query);
|
||||
if (movie == null)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("Failed to find that movie.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("imdb_fail").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
await Context.Channel.EmbedAsync(movie.GetEmbed()).ConfigureAwait(false);
|
||||
@ -94,7 +90,7 @@ namespace NadekoBot.Modules.Searches
|
||||
using (var http = new HttpClient())
|
||||
{
|
||||
var res = JObject.Parse(await http.GetStringAsync("http://www.random.cat/meow").ConfigureAwait(false));
|
||||
await Context.Channel.SendMessageAsync(res["file"].ToString()).ConfigureAwait(false);
|
||||
await Context.Channel.SendMessageAsync(Uri.EscapeUriString(res["file"].ToString())).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -122,12 +118,12 @@ namespace NadekoBot.Modules.Searches
|
||||
var res = await NadekoBot.Google.GetImageAsync(terms).ConfigureAwait(false);
|
||||
var embed = new EmbedBuilder()
|
||||
.WithOkColor()
|
||||
.WithAuthor(eab => eab.WithName("Image Search For: " + terms.TrimTo(50))
|
||||
.WithAuthor(eab => eab.WithName(GetText("image_search_for") + " " + terms.TrimTo(50))
|
||||
.WithUrl("https://www.google.rs/search?q=" + terms + "&source=lnms&tbm=isch")
|
||||
.WithIconUrl("http://i.imgur.com/G46fm8J.png"))
|
||||
.WithDescription(res.Link)
|
||||
.WithImageUrl(res.Link)
|
||||
.WithTitle(Context.User.Mention);
|
||||
.WithTitle(Context.User.ToString());
|
||||
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
@ -157,7 +153,7 @@ namespace NadekoBot.Modules.Searches
|
||||
.WithIconUrl("http://s.imgur.com/images/logo-1200-630.jpg?"))
|
||||
.WithDescription(source)
|
||||
.WithImageUrl(source)
|
||||
.WithTitle(Context.User.Mention);
|
||||
.WithTitle(Context.User.ToString());
|
||||
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@ -174,12 +170,12 @@ namespace NadekoBot.Modules.Searches
|
||||
var res = await NadekoBot.Google.GetImageAsync(terms, new NadekoRandom().Next(0, 50)).ConfigureAwait(false);
|
||||
var embed = new EmbedBuilder()
|
||||
.WithOkColor()
|
||||
.WithAuthor(eab => eab.WithName("Image Search For: " + terms.TrimTo(50))
|
||||
.WithAuthor(eab => eab.WithName(GetText("image_search_for") + " " + terms.TrimTo(50))
|
||||
.WithUrl("https://www.google.rs/search?q=" + terms + "&source=lnms&tbm=isch")
|
||||
.WithIconUrl("http://i.imgur.com/G46fm8J.png"))
|
||||
.WithDescription(res.Link)
|
||||
.WithImageUrl(res.Link)
|
||||
.WithTitle(Context.User.Mention);
|
||||
.WithTitle(Context.User.ToString());
|
||||
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
@ -205,12 +201,12 @@ namespace NadekoBot.Modules.Searches
|
||||
|
||||
var embed = new EmbedBuilder()
|
||||
.WithOkColor()
|
||||
.WithAuthor(eab => eab.WithName("Image Search For: " + terms.TrimTo(50))
|
||||
.WithAuthor(eab => eab.WithName(GetText("image_search_for") + " " + terms.TrimTo(50))
|
||||
.WithUrl(fullQueryLink)
|
||||
.WithIconUrl("http://s.imgur.com/images/logo-1200-630.jpg?"))
|
||||
.WithDescription(source)
|
||||
.WithImageUrl(source)
|
||||
.WithTitle(Context.User.Mention);
|
||||
.WithTitle(Context.User.ToString());
|
||||
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@ -235,13 +231,14 @@ namespace NadekoBot.Modules.Searches
|
||||
|
||||
if (shortened == arg)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("Failed to shorten that url.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("shorten_fail").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
await Context.Channel.EmbedAsync(new EmbedBuilder().WithColor(NadekoBot.OkColor)
|
||||
.AddField(efb => efb.WithName("Original Url")
|
||||
.AddField(efb => efb.WithName(GetText("original_url"))
|
||||
.WithValue($"<{arg}>"))
|
||||
.AddField(efb => efb.WithName("Short Url")
|
||||
.AddField(efb => efb.WithName(GetText("short_url"))
|
||||
.WithValue($"<{shortened}>")))
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
@ -273,7 +270,7 @@ namespace NadekoBot.Modules.Searches
|
||||
|
||||
var results = elems.Select<IElement, GoogleSearchResult?>(elem =>
|
||||
{
|
||||
var aTag = (elem.Children.FirstOrDefault().Children.FirstOrDefault() as IHtmlAnchorElement); // <h3> -> <a>
|
||||
var aTag = (elem.Children.FirstOrDefault()?.Children.FirstOrDefault() as IHtmlAnchorElement); // <h3> -> <a>
|
||||
var href = aTag?.Href;
|
||||
var name = aTag?.TextContent;
|
||||
if (href == null || name == null)
|
||||
@ -289,35 +286,31 @@ namespace NadekoBot.Modules.Searches
|
||||
|
||||
var embed = new EmbedBuilder()
|
||||
.WithOkColor()
|
||||
.WithAuthor(eab => eab.WithName("Search For: " + terms.TrimTo(50))
|
||||
.WithAuthor(eab => eab.WithName(GetText("search_for") + " " + terms.TrimTo(50))
|
||||
.WithUrl(fullQueryLink)
|
||||
.WithIconUrl("http://i.imgur.com/G46fm8J.png"))
|
||||
.WithTitle(Context.User.Mention)
|
||||
.WithTitle(Context.User.ToString())
|
||||
.WithFooter(efb => efb.WithText(totalResults));
|
||||
|
||||
var desc = await Task.WhenAll(results.Select(async res =>
|
||||
$"[{Format.Bold(res?.Title)}]({(await NadekoBot.Google.ShortenUrl(res?.Link))})\n{res?.Text}\n\n"))
|
||||
.ConfigureAwait(false);
|
||||
await Context.Channel.EmbedAsync(embed.WithDescription(String.Concat(desc))).ConfigureAwait(false);
|
||||
await Context.Channel.EmbedAsync(embed.WithDescription(string.Concat(desc))).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task MagicTheGathering([Remainder] string name = null)
|
||||
public async Task MagicTheGathering([Remainder] string name)
|
||||
{
|
||||
var arg = name;
|
||||
if (string.IsNullOrWhiteSpace(arg))
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("Please enter a card name to search for.").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
await Context.Channel.TriggerTypingAsync().ConfigureAwait(false);
|
||||
string response = "";
|
||||
using (var http = new HttpClient())
|
||||
{
|
||||
http.DefaultRequestHeaders.Clear();
|
||||
response = await http.GetStringAsync($"https://api.deckbrew.com/mtg/cards?name={Uri.EscapeUriString(arg)}")
|
||||
.ConfigureAwait(false);
|
||||
var response = await http.GetStringAsync($"https://api.deckbrew.com/mtg/cards?name={Uri.EscapeUriString(arg)}")
|
||||
.ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
var items = JArray.Parse(response).ToArray();
|
||||
@ -327,50 +320,46 @@ namespace NadekoBot.Modules.Searches
|
||||
var storeUrl = await NadekoBot.Google.ShortenUrl(item["store_url"].ToString());
|
||||
var cost = item["cost"].ToString();
|
||||
var desc = item["text"].ToString();
|
||||
var types = String.Join(",\n", item["types"].ToObject<string[]>());
|
||||
var types = string.Join(",\n", item["types"].ToObject<string[]>());
|
||||
var img = item["editions"][0]["image_url"].ToString();
|
||||
var embed = new EmbedBuilder().WithOkColor()
|
||||
.WithTitle(item["name"].ToString())
|
||||
.WithDescription(desc)
|
||||
.WithImageUrl(img)
|
||||
.AddField(efb => efb.WithName("Store Url").WithValue(storeUrl).WithIsInline(true))
|
||||
.AddField(efb => efb.WithName("Cost").WithValue(cost).WithIsInline(true))
|
||||
.AddField(efb => efb.WithName("Types").WithValue(types).WithIsInline(true));
|
||||
.AddField(efb => efb.WithName(GetText("store_url")).WithValue(storeUrl).WithIsInline(true))
|
||||
.AddField(efb => efb.WithName(GetText("cost")).WithValue(cost).WithIsInline(true))
|
||||
.AddField(efb => efb.WithName(GetText("types")).WithValue(types).WithIsInline(true));
|
||||
//.AddField(efb => efb.WithName("Store Url").WithValue(await NadekoBot.Google.ShortenUrl(items[0]["store_url"].ToString())).WithIsInline(true));
|
||||
|
||||
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await Context.Channel.SendErrorAsync($"Error could not find the card '{arg}'.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("card_not_found").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task Hearthstone([Remainder] string name = null)
|
||||
public async Task Hearthstone([Remainder] string name)
|
||||
{
|
||||
var arg = name;
|
||||
if (string.IsNullOrWhiteSpace(arg))
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("Please enter a card name to search for.").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.MashapeKey))
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("Bot owner didn't specify MashapeApiKey. You can't use this functionality.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("mashape_api_missing").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
await Context.Channel.TriggerTypingAsync().ConfigureAwait(false);
|
||||
string response = "";
|
||||
using (var http = new HttpClient())
|
||||
{
|
||||
http.DefaultRequestHeaders.Clear();
|
||||
http.DefaultRequestHeaders.Add("X-Mashape-Key", NadekoBot.Credentials.MashapeKey);
|
||||
response = await http.GetStringAsync($"https://omgvamp-hearthstone-v1.p.mashape.com/cards/search/{Uri.EscapeUriString(arg)}")
|
||||
.ConfigureAwait(false);
|
||||
var response = await http.GetStringAsync($"https://omgvamp-hearthstone-v1.p.mashape.com/cards/search/{Uri.EscapeUriString(arg)}")
|
||||
.ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
var items = JArray.Parse(response).Shuffle().ToList();
|
||||
@ -393,17 +382,17 @@ namespace NadekoBot.Modules.Searches
|
||||
string msg = null;
|
||||
if (items.Count > 4)
|
||||
{
|
||||
msg = "⚠ Found over 4 images. Showing random 4.";
|
||||
msg = GetText("hs_over_x", 4);
|
||||
}
|
||||
var ms = new MemoryStream();
|
||||
await Task.Run(() => images.AsEnumerable().Merge().SaveAsPng(ms));
|
||||
await Task.Run(() => images.AsEnumerable().Merge().Save(ms));
|
||||
ms.Position = 0;
|
||||
await Context.Channel.SendFileAsync(ms, arg + ".png", msg).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync($"Error occured.").ConfigureAwait(false);
|
||||
_log.Error(ex);
|
||||
await ReplyErrorLocalized("error_occured").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -413,23 +402,20 @@ namespace NadekoBot.Modules.Searches
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.MashapeKey))
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("Bot owner didn't specify MashapeApiKey. You can't use this functionality.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("mashape_api_missing").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
var arg = query;
|
||||
if (string.IsNullOrWhiteSpace(arg))
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("Please enter a sentence.").ConfigureAwait(false);
|
||||
if (string.IsNullOrWhiteSpace(query))
|
||||
return;
|
||||
}
|
||||
|
||||
await Context.Channel.TriggerTypingAsync().ConfigureAwait(false);
|
||||
using (var http = new HttpClient())
|
||||
{
|
||||
http.DefaultRequestHeaders.Clear();
|
||||
http.DefaultRequestHeaders.Add("X-Mashape-Key", NadekoBot.Credentials.MashapeKey);
|
||||
http.DefaultRequestHeaders.Add("Accept", "text/plain");
|
||||
var res = await http.GetStringAsync($"https://yoda.p.mashape.com/yoda?sentence={Uri.EscapeUriString(arg)}").ConfigureAwait(false);
|
||||
var res = await http.GetStringAsync($"https://yoda.p.mashape.com/yoda?sentence={Uri.EscapeUriString(query)}").ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
var embed = new EmbedBuilder()
|
||||
@ -441,7 +427,7 @@ namespace NadekoBot.Modules.Searches
|
||||
}
|
||||
catch
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("Failed to yodify your sentence.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("yodify_error").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -451,22 +437,19 @@ namespace NadekoBot.Modules.Searches
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.MashapeKey))
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("Bot owner didn't specify MashapeApiKey. You can't use this functionality.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("mashape_api_missing").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
var arg = query;
|
||||
if (string.IsNullOrWhiteSpace(arg))
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("Please enter a search term.").ConfigureAwait(false);
|
||||
if (string.IsNullOrWhiteSpace(query))
|
||||
return;
|
||||
}
|
||||
|
||||
await Context.Channel.TriggerTypingAsync().ConfigureAwait(false);
|
||||
using (var http = new HttpClient())
|
||||
{
|
||||
http.DefaultRequestHeaders.Clear();
|
||||
http.DefaultRequestHeaders.Add("Accept", "application/json");
|
||||
var res = await http.GetStringAsync($"http://api.urbandictionary.com/v0/define?term={Uri.EscapeUriString(arg)}").ConfigureAwait(false);
|
||||
var res = await http.GetStringAsync($"http://api.urbandictionary.com/v0/define?term={Uri.EscapeUriString(query)}").ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
var items = JObject.Parse(res);
|
||||
@ -482,7 +465,7 @@ namespace NadekoBot.Modules.Searches
|
||||
}
|
||||
catch
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("Failed finding a definition for that term.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("ud_error").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -499,56 +482,53 @@ namespace NadekoBot.Modules.Searches
|
||||
|
||||
var data = JsonConvert.DeserializeObject<DefineModel>(res);
|
||||
|
||||
var sense = data.Results.Where(x => x.Senses != null && x.Senses[0].Definition != null).FirstOrDefault()?.Senses[0];
|
||||
var sense = data.Results.FirstOrDefault(x => x.Senses?[0].Definition != null)?.Senses[0];
|
||||
|
||||
if (sense?.Definition == null)
|
||||
return;
|
||||
|
||||
string definition = sense.Definition.ToString();
|
||||
var definition = sense.Definition.ToString();
|
||||
if (!(sense.Definition is string))
|
||||
definition = ((JArray)JToken.Parse(sense.Definition.ToString())).First.ToString();
|
||||
|
||||
var embed = new EmbedBuilder().WithOkColor()
|
||||
.WithTitle("Define: " + word)
|
||||
.WithTitle(GetText("define") + " " + word)
|
||||
.WithDescription(definition)
|
||||
.WithFooter(efb => efb.WithText(sense.Gramatical_info?.type));
|
||||
|
||||
if (sense.Examples != null)
|
||||
embed.AddField(efb => efb.WithName("Example").WithValue(sense.Examples.First().text));
|
||||
embed.AddField(efb => efb.WithName(GetText("example")).WithValue(sense.Examples.First().text));
|
||||
|
||||
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task Hashtag([Remainder] string query = null)
|
||||
public async Task Hashtag([Remainder] string query)
|
||||
{
|
||||
var arg = query;
|
||||
if (string.IsNullOrWhiteSpace(arg))
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("Please enter a search term.").ConfigureAwait(false);
|
||||
if (string.IsNullOrWhiteSpace(query))
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.MashapeKey))
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("Bot owner didn't specify MashapeApiKey. You can't use this functionality.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("mashape_api_missing").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
await Context.Channel.TriggerTypingAsync().ConfigureAwait(false);
|
||||
string res = "";
|
||||
string res;
|
||||
using (var http = new HttpClient())
|
||||
{
|
||||
http.DefaultRequestHeaders.Clear();
|
||||
http.DefaultRequestHeaders.Add("X-Mashape-Key", NadekoBot.Credentials.MashapeKey);
|
||||
res = await http.GetStringAsync($"https://tagdef.p.mashape.com/one.{Uri.EscapeUriString(arg)}.json").ConfigureAwait(false);
|
||||
res = await http.GetStringAsync($"https://tagdef.p.mashape.com/one.{Uri.EscapeUriString(query)}.json").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var items = JObject.Parse(res);
|
||||
var item = items["defs"]["def"];
|
||||
var hashtag = item["hashtag"].ToString();
|
||||
//var hashtag = item["hashtag"].ToString();
|
||||
var link = item["uri"].ToString();
|
||||
var desc = item["text"].ToString();
|
||||
await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||
@ -560,7 +540,7 @@ namespace NadekoBot.Modules.Searches
|
||||
}
|
||||
catch
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("Failed finding a definition for that tag.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("hashtag_error").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -574,7 +554,7 @@ namespace NadekoBot.Modules.Searches
|
||||
return;
|
||||
|
||||
var fact = JObject.Parse(response)["facts"][0].ToString();
|
||||
await Context.Channel.SendConfirmAsync("🐈fact", fact).ConfigureAwait(false);
|
||||
await Context.Channel.SendConfirmAsync("🐈" + GetText("catfact"), fact).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -611,7 +591,7 @@ namespace NadekoBot.Modules.Searches
|
||||
var result = await http.GetStringAsync("https://en.wikipedia.org//w/api.php?action=query&format=json&prop=info&redirects=1&formatversion=2&inprop=url&titles=" + Uri.EscapeDataString(query));
|
||||
var data = JsonConvert.DeserializeObject<WikipediaApiModel>(result);
|
||||
if (data.Query.Pages[0].Missing)
|
||||
await Context.Channel.SendErrorAsync("That page could not be found.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("wiki_page_not_found").ConfigureAwait(false);
|
||||
else
|
||||
await Context.Channel.SendMessageAsync(data.Query.Pages[0].FullUrl).ConfigureAwait(false);
|
||||
}
|
||||
@ -625,28 +605,21 @@ namespace NadekoBot.Modules.Searches
|
||||
return;
|
||||
var img = new ImageSharp.Image(50, 50);
|
||||
|
||||
img.BackgroundColor(new ImageSharp.Color(color));
|
||||
img.ApplyProcessor(new BackgroundColorProcessor<ImageSharp.Color>(ImageSharp.Color.FromHex(color)), img.Bounds);
|
||||
|
||||
await Context.Channel.SendFileAsync(img.ToStream(), $"{color}.png").ConfigureAwait(false); ;
|
||||
await Context.Channel.SendFileAsync(img.ToStream(), $"{color}.png").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task Videocall([Remainder] params IUser[] users)
|
||||
public async Task Videocall(params IUser[] users)
|
||||
{
|
||||
try
|
||||
var allUsrs = users.Append(Context.User);
|
||||
var allUsrsArray = allUsrs.ToArray();
|
||||
var str = allUsrsArray.Aggregate("http://appear.in/", (current, usr) => current + Uri.EscapeUriString(usr.Username[0].ToString()));
|
||||
str += new NadekoRandom().Next();
|
||||
foreach (var usr in allUsrsArray)
|
||||
{
|
||||
var allUsrs = users.Append(Context.User);
|
||||
var allUsrsArray = allUsrs.ToArray();
|
||||
var str = allUsrsArray.Aggregate("http://appear.in/", (current, usr) => current + Uri.EscapeUriString(usr.Username[0].ToString()));
|
||||
str += new NadekoRandom().Next();
|
||||
foreach (var usr in allUsrsArray)
|
||||
{
|
||||
await (await (usr as IGuildUser).CreateDMChannelAsync()).SendConfirmAsync(str).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.Error(ex);
|
||||
await (await usr.CreateDMChannelAsync()).SendConfirmAsync(str).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -666,11 +639,11 @@ namespace NadekoBot.Modules.Searches
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task Wikia(string target, [Remainder] string query = null)
|
||||
public async Task Wikia(string target, [Remainder] string query)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(target) || string.IsNullOrWhiteSpace(query))
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("Please enter a target wikia, followed by search query.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("wikia_input_error").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
await Context.Channel.TriggerTypingAsync().ConfigureAwait(false);
|
||||
@ -682,90 +655,87 @@ namespace NadekoBot.Modules.Searches
|
||||
var res = await http.GetStringAsync($"http://www.{Uri.EscapeUriString(target)}.wikia.com/api/v1/Search/List?query={Uri.EscapeUriString(query)}&limit=25&minArticleQuality=10&batch=1&namespaces=0%2C14").ConfigureAwait(false);
|
||||
var items = JObject.Parse(res);
|
||||
var found = items["items"][0];
|
||||
var response = $@"`Title:` {found["title"].ToString()}
|
||||
`Quality:` {found["quality"]}
|
||||
`URL:` {await NadekoBot.Google.ShortenUrl(found["url"].ToString()).ConfigureAwait(false)}";
|
||||
var response = $@"`{GetText("title")}` {found["title"]}
|
||||
`{GetText("quality")}` {found["quality"]}
|
||||
`{GetText("url")}:` {await NadekoBot.Google.ShortenUrl(found["url"].ToString()).ConfigureAwait(false)}";
|
||||
await Context.Channel.SendMessageAsync(response).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await Context.Channel.SendErrorAsync($"Failed finding `{query}`.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("wikia_error").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task MCPing([Remainder] string query = null)
|
||||
{
|
||||
var arg = query;
|
||||
if (string.IsNullOrWhiteSpace(arg))
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("💢 Please enter a `ip:port`.").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
await Context.Channel.TriggerTypingAsync().ConfigureAwait(false);
|
||||
using (var http = new HttpClient())
|
||||
{
|
||||
http.DefaultRequestHeaders.Clear();
|
||||
string ip = arg.Split(':')[0];
|
||||
string port = arg.Split(':')[1];
|
||||
var res = await http.GetStringAsync($"https://api.minetools.eu/ping/{Uri.EscapeUriString(ip)}/{Uri.EscapeUriString(port)}").ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
var items = JObject.Parse(res);
|
||||
var sb = new StringBuilder();
|
||||
int ping = (int)Math.Ceiling(Double.Parse(items["latency"].ToString()));
|
||||
sb.AppendLine($"`Server:` {arg}");
|
||||
sb.AppendLine($"`Version:` {items["version"]["name"].ToString()} / Protocol {items["version"]["protocol"].ToString()}");
|
||||
sb.AppendLine($"`Description:` {items["description"].ToString()}");
|
||||
sb.AppendLine($"`Online Players:` {items["players"]["online"].ToString()}/{items["players"]["max"].ToString()}");
|
||||
sb.Append($"`Latency:` {ping}");
|
||||
await Context.Channel.SendMessageAsync(sb.ToString());
|
||||
}
|
||||
catch
|
||||
{
|
||||
await Context.Channel.SendErrorAsync($"Failed finding `{arg}`.").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
//[NadekoCommand, Usage, Description, Aliases]
|
||||
//public async Task MCPing([Remainder] string query2 = null)
|
||||
//{
|
||||
// var query = query2;
|
||||
// if (string.IsNullOrWhiteSpace(query))
|
||||
// return;
|
||||
// await Context.Channel.TriggerTypingAsync().ConfigureAwait(false);
|
||||
// using (var http = new HttpClient())
|
||||
// {
|
||||
// http.DefaultRequestHeaders.Clear();
|
||||
// var ip = query.Split(':')[0];
|
||||
// var port = query.Split(':')[1];
|
||||
// var res = await http.GetStringAsync($"https://api.minetools.eu/ping/{Uri.EscapeUriString(ip)}/{Uri.EscapeUriString(port)}").ConfigureAwait(false);
|
||||
// try
|
||||
// {
|
||||
// var items = JObject.Parse(res);
|
||||
// var sb = new StringBuilder();
|
||||
// var ping = (int)Math.Ceiling(double.Parse(items["latency"].ToString()));
|
||||
// sb.AppendLine($"`Server:` {query}");
|
||||
// sb.AppendLine($"`Version:` {items["version"]["name"]} / Protocol {items["version"]["protocol"]}");
|
||||
// sb.AppendLine($"`Description:` {items["description"]}");
|
||||
// sb.AppendLine($"`Online Players:` {items["players"]["online"]}/{items["players"]["max"]}");
|
||||
// sb.Append($"`Latency:` {ping}");
|
||||
// await Context.Channel.SendMessageAsync(sb.ToString());
|
||||
// }
|
||||
// catch
|
||||
// {
|
||||
// await Context.Channel.SendErrorAsync($"Failed finding `{query}`.").ConfigureAwait(false);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task MCQ([Remainder] string query = null)
|
||||
{
|
||||
var arg = query;
|
||||
if (string.IsNullOrWhiteSpace(arg))
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("Please enter `ip:port`.").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
await Context.Channel.TriggerTypingAsync().ConfigureAwait(false);
|
||||
using (var http = new HttpClient())
|
||||
{
|
||||
http.DefaultRequestHeaders.Clear();
|
||||
try
|
||||
{
|
||||
string ip = arg.Split(':')[0];
|
||||
string port = arg.Split(':')[1];
|
||||
var res = await http.GetStringAsync($"https://api.minetools.eu/query/{Uri.EscapeUriString(ip)}/{Uri.EscapeUriString(port)}").ConfigureAwait(false);
|
||||
var items = JObject.Parse(res);
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine($"`Server:` {arg.ToString()} 〘Status: {items["status"]}〙");
|
||||
sb.AppendLine($"`Player List (First 5):`");
|
||||
foreach (var item in items["Playerlist"].Take(5))
|
||||
{
|
||||
sb.AppendLine($"〔:rosette: {item}〕");
|
||||
}
|
||||
sb.AppendLine($"`Online Players:` {items["Players"]} / {items["MaxPlayers"]}");
|
||||
sb.AppendLine($"`Plugins:` {items["Plugins"]}");
|
||||
sb.Append($"`Version:` {items["Version"]}");
|
||||
await Context.Channel.SendMessageAsync(sb.ToString());
|
||||
}
|
||||
catch
|
||||
{
|
||||
await Context.Channel.SendErrorAsync($"Failed finding server `{arg}`.").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
//[NadekoCommand, Usage, Description, Aliases]
|
||||
//public async Task MCQ([Remainder] string query = null)
|
||||
//{
|
||||
// var arg = query;
|
||||
// if (string.IsNullOrWhiteSpace(arg))
|
||||
// {
|
||||
// await Context.Channel.SendErrorAsync("Please enter `ip:port`.").ConfigureAwait(false);
|
||||
// return;
|
||||
// }
|
||||
// await Context.Channel.TriggerTypingAsync().ConfigureAwait(false);
|
||||
// using (var http = new HttpClient())
|
||||
// {
|
||||
// http.DefaultRequestHeaders.Clear();
|
||||
// try
|
||||
// {
|
||||
// var ip = arg.Split(':')[0];
|
||||
// var port = arg.Split(':')[1];
|
||||
// var res = await http.GetStringAsync($"https://api.minetools.eu/query/{Uri.EscapeUriString(ip)}/{Uri.EscapeUriString(port)}").ConfigureAwait(false);
|
||||
// var items = JObject.Parse(res);
|
||||
// var sb = new StringBuilder();
|
||||
// sb.AppendLine($"`Server:` {arg} 〘Status: {items["status"]}〙");
|
||||
// sb.AppendLine("`Player List (First 5):`");
|
||||
// foreach (var item in items["Playerlist"].Take(5))
|
||||
// {
|
||||
// sb.AppendLine($"〔:rosette: {item}〕");
|
||||
// }
|
||||
// sb.AppendLine($"`Online Players:` {items["Players"]} / {items["MaxPlayers"]}");
|
||||
// sb.AppendLine($"`Plugins:` {items["Plugins"]}");
|
||||
// sb.Append($"`Version:` {items["Version"]}");
|
||||
// await Context.Channel.SendMessageAsync(sb.ToString());
|
||||
// }
|
||||
// catch
|
||||
// {
|
||||
// await Context.Channel.SendErrorAsync($"Failed finding server `{arg}`.").ConfigureAwait(false);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
public enum DapiSearchType
|
||||
{
|
||||
@ -776,7 +746,7 @@ namespace NadekoBot.Modules.Searches
|
||||
Yandere
|
||||
}
|
||||
|
||||
public static async Task InternalDapiCommand(IUserMessage umsg, string tag, DapiSearchType type)
|
||||
public async Task InternalDapiCommand(IUserMessage umsg, string tag, DapiSearchType type)
|
||||
{
|
||||
var channel = umsg.Channel;
|
||||
|
||||
@ -785,7 +755,7 @@ namespace NadekoBot.Modules.Searches
|
||||
var url = await InternalDapiSearch(tag, type).ConfigureAwait(false);
|
||||
|
||||
if (url == null)
|
||||
await channel.SendErrorAsync(umsg.Author.Mention + " No results.");
|
||||
await channel.SendErrorAsync(umsg.Author.Mention + " " + GetText("no_results"));
|
||||
else
|
||||
await channel.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||
.WithDescription(umsg.Author.Mention + " " + tag)
|
||||
@ -796,7 +766,7 @@ namespace NadekoBot.Modules.Searches
|
||||
public static async Task<string> InternalDapiSearch(string tag, DapiSearchType type)
|
||||
{
|
||||
tag = tag?.Replace(" ", "_");
|
||||
string website = "";
|
||||
var website = "";
|
||||
switch (type)
|
||||
{
|
||||
case DapiSearchType.Safebooru:
|
||||
@ -841,10 +811,10 @@ namespace NadekoBot.Modules.Searches
|
||||
return null;
|
||||
}
|
||||
}
|
||||
public static async Task<bool> ValidateQuery(IMessageChannel ch, string query)
|
||||
public async Task<bool> ValidateQuery(IMessageChannel ch, string query)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(query.Trim())) return true;
|
||||
await ch.SendErrorAsync("Please specify search parameters.").ConfigureAwait(false);
|
||||
if (!string.IsNullOrWhiteSpace(query)) return true;
|
||||
await ch.SendErrorAsync(GetText("specify_search_params")).ConfigureAwait(false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ namespace NadekoBot.Modules.Utility
|
||||
public partial class Utility
|
||||
{
|
||||
[Group]
|
||||
public class CalcCommands : ModuleBase
|
||||
public class CalcCommands : NadekoSubmodule
|
||||
{
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task Calculate([Remainder] string expression)
|
||||
@ -21,9 +21,9 @@ namespace NadekoBot.Modules.Utility
|
||||
expr.EvaluateParameter += Expr_EvaluateParameter;
|
||||
var result = expr.Evaluate();
|
||||
if (expr.Error == null)
|
||||
await Context.Channel.SendConfirmAsync("Result", $"{result}");
|
||||
await Context.Channel.SendConfirmAsync("⚙ " + GetText("result"), result.ToString());
|
||||
else
|
||||
await Context.Channel.SendErrorAsync($"⚙ Error", expr.Error);
|
||||
await Context.Channel.SendErrorAsync("⚙ " + GetText("error"), expr.Error);
|
||||
}
|
||||
|
||||
private static void Expr_EvaluateParameter(string name, NCalc.ParameterArgs args)
|
||||
@ -42,29 +42,26 @@ namespace NadekoBot.Modules.Utility
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task CalcOps()
|
||||
{
|
||||
var selection = typeof(Math).GetTypeInfo().GetMethods().Except(typeof(object).GetTypeInfo().GetMethods()).Distinct(new MethodInfoEqualityComparer()).Select(x =>
|
||||
{
|
||||
return x.Name;
|
||||
})
|
||||
.Except(new[] { "ToString",
|
||||
"Equals",
|
||||
"GetHashCode",
|
||||
"GetType"});
|
||||
await Context.Channel.SendConfirmAsync("Available functions in calc", string.Join(", ", selection));
|
||||
var selection = typeof(Math).GetTypeInfo()
|
||||
.GetMethods()
|
||||
.Distinct(new MethodInfoEqualityComparer())
|
||||
.Select(x => x.Name)
|
||||
.Except(new[]
|
||||
{
|
||||
"ToString",
|
||||
"Equals",
|
||||
"GetHashCode",
|
||||
"GetType"
|
||||
});
|
||||
await Context.Channel.SendConfirmAsync(GetText("utility_calcops", Prefix), string.Join(", ", selection));
|
||||
}
|
||||
}
|
||||
|
||||
class MethodInfoEqualityComparer : IEqualityComparer<MethodInfo>
|
||||
private class MethodInfoEqualityComparer : IEqualityComparer<MethodInfo>
|
||||
{
|
||||
public bool Equals(MethodInfo x, MethodInfo y) => x.Name == y.Name;
|
||||
|
||||
public int GetHashCode(MethodInfo obj) => obj.Name.GetHashCode();
|
||||
}
|
||||
|
||||
class ExpressionContext
|
||||
{
|
||||
public double Pi { get; set; } = Math.PI;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -3,8 +3,6 @@ using Discord.Commands;
|
||||
using NadekoBot.Attributes;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Services;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
@ -14,12 +12,11 @@ namespace NadekoBot.Modules.Utility
|
||||
public partial class Utility
|
||||
{
|
||||
[Group]
|
||||
public class CrossServerTextChannel : ModuleBase
|
||||
public class CrossServerTextChannel : NadekoSubmodule
|
||||
{
|
||||
static CrossServerTextChannel()
|
||||
{
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
NadekoBot.Client.MessageReceived += async (imsg) =>
|
||||
NadekoBot.Client.MessageReceived += async imsg =>
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -37,23 +34,32 @@ namespace NadekoBot.Modules.Utility
|
||||
var set = subscriber.Value;
|
||||
if (!set.Contains(channel))
|
||||
continue;
|
||||
foreach (var chan in set.Except(new[] { channel }))
|
||||
foreach (var chan in set.Except(new[] {channel}))
|
||||
{
|
||||
try { await chan.SendMessageAsync(GetText(channel.Guild, channel, (IGuildUser)msg.Author, msg)).ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
|
||||
try
|
||||
{
|
||||
await chan.SendMessageAsync(GetMessage(channel, (IGuildUser) msg.Author,
|
||||
msg)).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
_log.Warn(ex);
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static string GetText(IGuild server, ITextChannel channel, IGuildUser user, IUserMessage message) =>
|
||||
$"**{server.Name} | {channel.Name}** `{user.Username}`: " + message.Content.SanitizeMentions();
|
||||
|
||||
public static readonly ConcurrentDictionary<int, ConcurrentHashSet<ITextChannel>> Subscribers = new ConcurrentDictionary<int, ConcurrentHashSet<ITextChannel>>();
|
||||
private static Logger _log { get; }
|
||||
private static string GetMessage(ITextChannel channel, IGuildUser user, IUserMessage message) =>
|
||||
$"**{channel.Guild.Name} | {channel.Name}** `{user.Username}`: " + message.Content.SanitizeMentions();
|
||||
|
||||
public static readonly ConcurrentDictionary<int, ConcurrentHashSet<ITextChannel>> Subscribers =
|
||||
new ConcurrentDictionary<int, ConcurrentHashSet<ITextChannel>>();
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
@ -64,8 +70,9 @@ namespace NadekoBot.Modules.Utility
|
||||
var set = new ConcurrentHashSet<ITextChannel>();
|
||||
if (Subscribers.TryAdd(token, set))
|
||||
{
|
||||
set.Add((ITextChannel)Context.Channel);
|
||||
await ((IGuildUser)Context.User).SendConfirmAsync("This is your CSC token", token.ToString()).ConfigureAwait(false);
|
||||
set.Add((ITextChannel) Context.Channel);
|
||||
await ((IGuildUser) Context.User).SendConfirmAsync(GetText("csc_token"), token.ToString())
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -77,8 +84,8 @@ namespace NadekoBot.Modules.Utility
|
||||
ConcurrentHashSet<ITextChannel> set;
|
||||
if (!Subscribers.TryGetValue(token, out set))
|
||||
return;
|
||||
set.Add((ITextChannel)Context.Channel);
|
||||
await Context.Channel.SendConfirmAsync("Joined cross server channel.").ConfigureAwait(false);
|
||||
set.Add((ITextChannel) Context.Channel);
|
||||
await ReplyConfirmLocalized("csc_join").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -88,9 +95,9 @@ namespace NadekoBot.Modules.Utility
|
||||
{
|
||||
foreach (var subscriber in Subscribers)
|
||||
{
|
||||
subscriber.Value.TryRemove((ITextChannel)Context.Channel);
|
||||
subscriber.Value.TryRemove((ITextChannel) Context.Channel);
|
||||
}
|
||||
await Context.Channel.SendMessageAsync("Left cross server channel.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("csc_leave").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ namespace NadekoBot.Modules.Utility
|
||||
public partial class Utility
|
||||
{
|
||||
[Group]
|
||||
public class InfoCommands : ModuleBase
|
||||
public class InfoCommands : NadekoSubmodule
|
||||
{
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
@ -24,7 +24,7 @@ namespace NadekoBot.Modules.Utility
|
||||
if (string.IsNullOrWhiteSpace(guildName))
|
||||
guild = channel.Guild;
|
||||
else
|
||||
guild = NadekoBot.Client.GetGuilds().Where(g => g.Name.ToUpperInvariant() == guildName.ToUpperInvariant()).FirstOrDefault();
|
||||
guild = NadekoBot.Client.GetGuilds().FirstOrDefault(g => g.Name.ToUpperInvariant() == guildName.ToUpperInvariant());
|
||||
if (guild == null)
|
||||
return;
|
||||
var ownername = await guild.GetUserAsync(guild.OwnerId);
|
||||
@ -32,28 +32,27 @@ namespace NadekoBot.Modules.Utility
|
||||
var voicechn = (await guild.GetVoiceChannelsAsync()).Count();
|
||||
|
||||
var createdAt = new DateTime(2015, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(guild.Id >> 22);
|
||||
var sb = new StringBuilder();
|
||||
var users = await guild.GetUsersAsync().ConfigureAwait(false);
|
||||
var features = string.Join("\n", guild.Features);
|
||||
if (string.IsNullOrWhiteSpace(features))
|
||||
features = "-";
|
||||
var embed = new EmbedBuilder()
|
||||
.WithAuthor(eab => eab.WithName("Server Info"))
|
||||
.WithAuthor(eab => eab.WithName(GetText("server_info")))
|
||||
.WithTitle(guild.Name)
|
||||
.AddField(fb => fb.WithName("**ID**").WithValue(guild.Id.ToString()).WithIsInline(true))
|
||||
.AddField(fb => fb.WithName("**Owner**").WithValue(ownername.ToString()).WithIsInline(true))
|
||||
.AddField(fb => fb.WithName("**Members**").WithValue(users.Count.ToString()).WithIsInline(true))
|
||||
.AddField(fb => fb.WithName("**Text Channels**").WithValue(textchn.ToString()).WithIsInline(true))
|
||||
.AddField(fb => fb.WithName("**Voice Channels**").WithValue(voicechn.ToString()).WithIsInline(true))
|
||||
.AddField(fb => fb.WithName("**Created At**").WithValue($"{createdAt.ToString("dd.MM.yyyy HH:mm")}").WithIsInline(true))
|
||||
.AddField(fb => fb.WithName("**Region**").WithValue(guild.VoiceRegionId.ToString()).WithIsInline(true))
|
||||
.AddField(fb => fb.WithName("**Roles**").WithValue((guild.Roles.Count - 1).ToString()).WithIsInline(true))
|
||||
.AddField(fb => fb.WithName("**Features**").WithValue(features).WithIsInline(true))
|
||||
.AddField(fb => fb.WithName(GetText("id")).WithValue(guild.Id.ToString()).WithIsInline(true))
|
||||
.AddField(fb => fb.WithName(GetText("owner")).WithValue(ownername.ToString()).WithIsInline(true))
|
||||
.AddField(fb => fb.WithName(GetText("members")).WithValue(users.Count.ToString()).WithIsInline(true))
|
||||
.AddField(fb => fb.WithName(GetText("text_channels")).WithValue(textchn.ToString()).WithIsInline(true))
|
||||
.AddField(fb => fb.WithName(GetText("voice_channels")).WithValue(voicechn.ToString()).WithIsInline(true))
|
||||
.AddField(fb => fb.WithName(GetText("created_at")).WithValue($"{createdAt:dd.MM.yyyy HH:mm}").WithIsInline(true))
|
||||
.AddField(fb => fb.WithName(GetText("region")).WithValue(guild.VoiceRegionId.ToString()).WithIsInline(true))
|
||||
.AddField(fb => fb.WithName(GetText("roles")).WithValue((guild.Roles.Count - 1).ToString()).WithIsInline(true))
|
||||
.AddField(fb => fb.WithName(GetText("features")).WithValue(features).WithIsInline(true))
|
||||
.WithImageUrl(guild.IconUrl)
|
||||
.WithColor(NadekoBot.OkColor);
|
||||
if (guild.Emojis.Count() > 0)
|
||||
if (guild.Emojis.Any())
|
||||
{
|
||||
embed.AddField(fb => fb.WithName($"**Custom Emojis ({guild.Emojis.Count})**").WithValue(string.Join(" ", guild.Emojis.Shuffle().Take(25).Select(e => $"{e.Name} <:{e.Name}:{e.Id}>"))));
|
||||
embed.AddField(fb => fb.WithName(GetText("custom_emojis") + $"({guild.Emojis.Count})").WithValue(string.Join(" ", guild.Emojis.Shuffle().Take(20).Select(e => $"{e.Name} <:{e.Name}:{e.Id}>"))));
|
||||
}
|
||||
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
|
||||
}
|
||||
@ -70,9 +69,9 @@ namespace NadekoBot.Modules.Utility
|
||||
var embed = new EmbedBuilder()
|
||||
.WithTitle(ch.Name)
|
||||
.WithDescription(ch.Topic?.SanitizeMentions())
|
||||
.AddField(fb => fb.WithName("**ID**").WithValue(ch.Id.ToString()).WithIsInline(true))
|
||||
.AddField(fb => fb.WithName("**Created At**").WithValue($"{createdAt.ToString("dd.MM.yyyy HH:mm")}").WithIsInline(true))
|
||||
.AddField(fb => fb.WithName("**Users**").WithValue(usercount.ToString()).WithIsInline(true))
|
||||
.AddField(fb => fb.WithName(GetText("id")).WithValue(ch.Id.ToString()).WithIsInline(true))
|
||||
.AddField(fb => fb.WithName(GetText("created_at")).WithValue($"{createdAt:dd.MM.yyyy HH:mm}").WithIsInline(true))
|
||||
.AddField(fb => fb.WithName(GetText("users")).WithValue(usercount.ToString()).WithIsInline(true))
|
||||
.WithColor(NadekoBot.OkColor);
|
||||
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
|
||||
}
|
||||
@ -81,22 +80,21 @@ namespace NadekoBot.Modules.Utility
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task UserInfo(IGuildUser usr = null)
|
||||
{
|
||||
var channel = (ITextChannel)Context.Channel;
|
||||
var user = usr ?? Context.User as IGuildUser;
|
||||
|
||||
if (user == null)
|
||||
return;
|
||||
|
||||
var embed = new EmbedBuilder()
|
||||
.AddField(fb => fb.WithName("**Name**").WithValue($"**{user.Username}**#{user.Discriminator}").WithIsInline(true));
|
||||
.AddField(fb => fb.WithName(GetText("name")).WithValue($"**{user.Username}**#{user.Discriminator}").WithIsInline(true));
|
||||
if (!string.IsNullOrWhiteSpace(user.Nickname))
|
||||
{
|
||||
embed.AddField(fb => fb.WithName("**Nickname**").WithValue(user.Nickname).WithIsInline(true));
|
||||
embed.AddField(fb => fb.WithName(GetText("nickname")).WithValue(user.Nickname).WithIsInline(true));
|
||||
}
|
||||
embed.AddField(fb => fb.WithName("**ID**").WithValue(user.Id.ToString()).WithIsInline(true))
|
||||
.AddField(fb => fb.WithName("**Joined Server**").WithValue($"{user.JoinedAt?.ToString("dd.MM.yyyy HH:mm")}").WithIsInline(true))
|
||||
.AddField(fb => fb.WithName("**Joined Discord**").WithValue($"{user.CreatedAt.ToString("dd.MM.yyyy HH:mm")}").WithIsInline(true))
|
||||
.AddField(fb => fb.WithName("**Roles**").WithValue($"**({user.RoleIds.Count - 1})** - {string.Join("\n", user.GetRoles().Where(r => r.Id != r.Guild.EveryoneRole.Id).Select(r => r.Name)).SanitizeMentions()}").WithIsInline(true))
|
||||
embed.AddField(fb => fb.WithName(GetText("id")).WithValue(user.Id.ToString()).WithIsInline(true))
|
||||
.AddField(fb => fb.WithName(GetText("joined_server")).WithValue($"{user.JoinedAt?.ToString("dd.MM.yyyy HH:mm") ?? "?"}").WithIsInline(true))
|
||||
.AddField(fb => fb.WithName(GetText("joined_discord")).WithValue($"{user.CreatedAt:dd.MM.yyyy HH:mm}").WithIsInline(true))
|
||||
.AddField(fb => fb.WithName(GetText("roles")).WithValue($"**({user.RoleIds.Count - 1})** - {string.Join("\n", user.GetRoles().Take(10).Where(r => r.Id != r.Guild.EveryoneRole.Id).Select(r => r.Name)).SanitizeMentions()}").WithIsInline(true))
|
||||
.WithColor(NadekoBot.OkColor);
|
||||
|
||||
if (user.AvatarId != null)
|
||||
@ -121,12 +119,17 @@ namespace NadekoBot.Modules.Utility
|
||||
StringBuilder str = new StringBuilder();
|
||||
foreach (var kvp in NadekoBot.CommandHandler.UserMessagesSent.OrderByDescending(kvp => kvp.Value).Skip(page*activityPerPage).Take(activityPerPage))
|
||||
{
|
||||
str.AppendLine($"`{++startCount}.` **{kvp.Key}** [{kvp.Value/NadekoBot.Stats.GetUptime().TotalSeconds:F2}/s] - {kvp.Value} total");
|
||||
str.AppendLine(GetText("activity_line",
|
||||
++startCount,
|
||||
Format.Bold(kvp.Key.ToString()),
|
||||
kvp.Value / NadekoBot.Stats.GetUptime().TotalSeconds, kvp.Value));
|
||||
}
|
||||
|
||||
await Context.Channel.EmbedAsync(new EmbedBuilder().WithTitle($"Activity Page #{page}")
|
||||
await Context.Channel.EmbedAsync(new EmbedBuilder()
|
||||
.WithTitle(GetText("activity_page", page))
|
||||
.WithOkColor()
|
||||
.WithFooter(efb => efb.WithText($"{NadekoBot.CommandHandler.UserMessagesSent.Count} users total."))
|
||||
.WithFooter(efb => efb.WithText(GetText("activity_users_total",
|
||||
NadekoBot.CommandHandler.UserMessagesSent.Count)))
|
||||
.WithDescription(str.ToString()));
|
||||
}
|
||||
}
|
||||
|
@ -5,45 +5,47 @@ using Microsoft.EntityFrameworkCore;
|
||||
using NadekoBot.Attributes;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Services;
|
||||
using NadekoBot.Services.Database;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Discord.WebSocket;
|
||||
|
||||
namespace NadekoBot.Modules.Utility
|
||||
{
|
||||
public partial class Utility
|
||||
{
|
||||
[Group]
|
||||
public class RepeatCommands : ModuleBase
|
||||
public class RepeatCommands : NadekoSubmodule
|
||||
{
|
||||
//guildid/RepeatRunners
|
||||
public static ConcurrentDictionary<ulong, ConcurrentQueue<RepeatRunner>> repeaters { get; }
|
||||
public static ConcurrentDictionary<ulong, ConcurrentQueue<RepeatRunner>> Repeaters { get; set; }
|
||||
|
||||
private static bool _ready;
|
||||
|
||||
public class RepeatRunner
|
||||
{
|
||||
private Logger _log { get; }
|
||||
private readonly Logger _log;
|
||||
|
||||
private CancellationTokenSource source { get; set; }
|
||||
private CancellationToken token { get; set; }
|
||||
public Repeater Repeater { get; }
|
||||
public ITextChannel Channel { get; }
|
||||
public SocketGuild Guild { get; }
|
||||
public ITextChannel Channel { get; private set; }
|
||||
|
||||
public RepeatRunner(Repeater repeater, ITextChannel channel = null)
|
||||
{
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
this.Repeater = repeater;
|
||||
this.Channel = channel ?? NadekoBot.Client.GetGuild(repeater.GuildId)?.GetTextChannel(repeater.ChannelId);
|
||||
if (Channel == null)
|
||||
return;
|
||||
Task.Run(Run);
|
||||
Repeater = repeater;
|
||||
Channel = channel;
|
||||
|
||||
Guild = NadekoBot.Client.GetGuild(repeater.GuildId);
|
||||
if(Guild!=null)
|
||||
Task.Run(Run);
|
||||
}
|
||||
|
||||
|
||||
@ -64,10 +66,21 @@ namespace NadekoBot.Modules.Utility
|
||||
// continue;
|
||||
|
||||
if (oldMsg != null)
|
||||
try { await oldMsg.DeleteAsync(); } catch { }
|
||||
try
|
||||
{
|
||||
await oldMsg.DeleteAsync();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
try
|
||||
{
|
||||
oldMsg = await Channel.SendMessageAsync(toSend).ConfigureAwait(false);
|
||||
if (Channel == null)
|
||||
Channel = Guild.GetTextChannel(Repeater.ChannelId);
|
||||
|
||||
if (Channel != null)
|
||||
oldMsg = await Channel.SendMessageAsync(toSend).ConfigureAwait(false);
|
||||
}
|
||||
catch (HttpException ex) when (ex.HttpCode == System.Net.HttpStatusCode.Forbidden)
|
||||
{
|
||||
@ -85,13 +98,15 @@ namespace NadekoBot.Modules.Utility
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException) { }
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
source.Cancel();
|
||||
var t = Task.Run(Run);
|
||||
var _ = Task.Run(Run);
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
@ -101,22 +116,23 @@ namespace NadekoBot.Modules.Utility
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{this.Channel.Mention} | {(int)this.Repeater.Interval.TotalHours}:{this.Repeater.Interval:mm} | {this.Repeater.Message.TrimTo(33)}";
|
||||
return
|
||||
$"{Channel.Mention} | {(int) Repeater.Interval.TotalHours}:{Repeater.Interval:mm} | {Repeater.Message.TrimTo(33)}";
|
||||
}
|
||||
}
|
||||
|
||||
static RepeatCommands()
|
||||
{
|
||||
var _log = LogManager.GetCurrentClassLogger();
|
||||
var sw = Stopwatch.StartNew();
|
||||
|
||||
repeaters = new ConcurrentDictionary<ulong, ConcurrentQueue<RepeatRunner>>(NadekoBot.AllGuildConfigs
|
||||
.ToDictionary(gc => gc.GuildId,
|
||||
gc => new ConcurrentQueue<RepeatRunner>(gc.GuildRepeaters.Select(gr => new RepeatRunner(gr))
|
||||
.Where(gr => gr.Channel != null))));
|
||||
|
||||
sw.Stop();
|
||||
_log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s");
|
||||
var _ = Task.Run(async () =>
|
||||
{
|
||||
await Task.Delay(5000).ConfigureAwait(false);
|
||||
Repeaters = new ConcurrentDictionary<ulong, ConcurrentQueue<RepeatRunner>>(NadekoBot.AllGuildConfigs
|
||||
.ToDictionary(gc => gc.GuildId,
|
||||
gc => new ConcurrentQueue<RepeatRunner>(gc.GuildRepeaters
|
||||
.Select(gr => new RepeatRunner(gr))
|
||||
.Where(x => x.Guild != null))));
|
||||
_ready = true;
|
||||
});
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -124,11 +140,13 @@ namespace NadekoBot.Modules.Utility
|
||||
[RequireUserPermission(GuildPermission.ManageMessages)]
|
||||
public async Task RepeatInvoke(int index)
|
||||
{
|
||||
if (!_ready)
|
||||
return;
|
||||
index -= 1;
|
||||
ConcurrentQueue<RepeatRunner> rep;
|
||||
if (!repeaters.TryGetValue(Context.Guild.Id, out rep))
|
||||
if (!Repeaters.TryGetValue(Context.Guild.Id, out rep))
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("ℹ️ **No repeating message found on this server.**").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("repeat_invoke_none").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -136,11 +154,12 @@ namespace NadekoBot.Modules.Utility
|
||||
|
||||
if (index >= repList.Count)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("Index out of range.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("index_out_of_range").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
var repeater = repList[index].Repeater;
|
||||
|
||||
repList[index].Reset();
|
||||
await Context.Channel.SendMessageAsync("🔄 " + repeater.Message).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@ -150,19 +169,21 @@ namespace NadekoBot.Modules.Utility
|
||||
[Priority(0)]
|
||||
public async Task RepeatRemove(int index)
|
||||
{
|
||||
if (!_ready)
|
||||
return;
|
||||
if (index < 1)
|
||||
return;
|
||||
index -= 1;
|
||||
|
||||
ConcurrentQueue<RepeatRunner> rep;
|
||||
if (!repeaters.TryGetValue(Context.Guild.Id, out rep))
|
||||
if (!Repeaters.TryGetValue(Context.Guild.Id, out rep))
|
||||
return;
|
||||
|
||||
var repeaterList = rep.ToList();
|
||||
|
||||
if (index >= repeaterList.Count)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("Index out of range.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("index_out_of_range").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -178,8 +199,9 @@ namespace NadekoBot.Modules.Utility
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (repeaters.TryUpdate(Context.Guild.Id, new ConcurrentQueue<RepeatRunner>(repeaterList), rep))
|
||||
await Context.Channel.SendConfirmAsync("Message Repeater",$"#{index+1} stopped.\n\n{repeater.ToString()}").ConfigureAwait(false);
|
||||
if (Repeaters.TryUpdate(Context.Guild.Id, new ConcurrentQueue<RepeatRunner>(repeaterList), rep))
|
||||
await Context.Channel.SendConfirmAsync(GetText("message_repeater"),
|
||||
GetText("repeater_stopped", index + 1) + $"\n\n{repeater}").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -188,6 +210,8 @@ namespace NadekoBot.Modules.Utility
|
||||
[Priority(1)]
|
||||
public async Task Repeat(int minutes, [Remainder] string message)
|
||||
{
|
||||
if (!_ready)
|
||||
return;
|
||||
if (minutes < 1 || minutes > 10080)
|
||||
return;
|
||||
|
||||
@ -213,15 +237,20 @@ namespace NadekoBot.Modules.Utility
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
var rep = new RepeatRunner(toAdd, (ITextChannel)Context.Channel);
|
||||
var rep = new RepeatRunner(toAdd, (ITextChannel) Context.Channel);
|
||||
|
||||
repeaters.AddOrUpdate(Context.Guild.Id, new ConcurrentQueue<RepeatRunner>(new[] { rep }), (key, old) =>
|
||||
Repeaters.AddOrUpdate(Context.Guild.Id, new ConcurrentQueue<RepeatRunner>(new[] {rep}), (key, old) =>
|
||||
{
|
||||
old.Enqueue(rep);
|
||||
return old;
|
||||
});
|
||||
|
||||
await Context.Channel.SendConfirmAsync($"🔁 Repeating **\"{rep.Repeater.Message}\"** every `{rep.Repeater.Interval.Days} day(s), {rep.Repeater.Interval.Hours} hour(s) and {rep.Repeater.Interval.Minutes} minute(s)`.").ConfigureAwait(false);
|
||||
await Context.Channel.SendConfirmAsync(
|
||||
"🔁 " + GetText("repeater",
|
||||
Format.Bold(rep.Repeater.Message),
|
||||
Format.Bold(rep.Repeater.Interval.Days.ToString()),
|
||||
Format.Bold(rep.Repeater.Interval.Hours.ToString()),
|
||||
Format.Bold(rep.Repeater.Interval.Minutes.ToString()))).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -229,27 +258,33 @@ namespace NadekoBot.Modules.Utility
|
||||
[RequireUserPermission(GuildPermission.ManageMessages)]
|
||||
public async Task RepeatList()
|
||||
{
|
||||
if (!_ready)
|
||||
return;
|
||||
ConcurrentQueue<RepeatRunner> repRunners;
|
||||
if (!repeaters.TryGetValue(Context.Guild.Id, out repRunners))
|
||||
if (!Repeaters.TryGetValue(Context.Guild.Id, out repRunners))
|
||||
{
|
||||
await Context.Channel.SendConfirmAsync("No repeaters running on this server.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("repeaters_none").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
var replist = repRunners.ToList();
|
||||
var sb = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < replist.Count; i++)
|
||||
for (var i = 0; i < replist.Count; i++)
|
||||
{
|
||||
var rep = replist[i];
|
||||
|
||||
sb.AppendLine($"`{i + 1}.` {rep.ToString()}");
|
||||
sb.AppendLine($"`{i + 1}.` {rep}");
|
||||
}
|
||||
var desc = sb.ToString();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(desc))
|
||||
desc = GetText("no_active_repeaters");
|
||||
|
||||
await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||
.WithTitle("List Of Repeaters")
|
||||
.WithDescription(sb.ToString()))
|
||||
.ConfigureAwait(false);
|
||||
.WithTitle(GetText("list_of_repeaters"))
|
||||
.WithDescription(desc))
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,13 +8,14 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using NadekoBot.DataStructures;
|
||||
|
||||
namespace NadekoBot.Modules.Utility
|
||||
{
|
||||
public partial class Utility
|
||||
{
|
||||
[Group]
|
||||
public class QuoteCommands : ModuleBase
|
||||
public class QuoteCommands : NadekoSubmodule
|
||||
{
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
@ -32,10 +33,11 @@ namespace NadekoBot.Modules.Utility
|
||||
}
|
||||
|
||||
if (quotes.Any())
|
||||
await Context.Channel.SendConfirmAsync($"💬 **Page {page + 1} of quotes:**\n```xl\n" + String.Join("\n", quotes.Select((q) => $"{q.Keyword,-20} by {q.AuthorName}")) + "\n```")
|
||||
await Context.Channel.SendConfirmAsync(GetText("quotes_page", page + 1),
|
||||
string.Join("\n", quotes.Select(q => $"{q.Keyword,-20} by {q.AuthorName}")))
|
||||
.ConfigureAwait(false);
|
||||
else
|
||||
await Context.Channel.SendErrorAsync("No quotes on this page.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("quotes_page_none").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -56,8 +58,40 @@ namespace NadekoBot.Modules.Utility
|
||||
if (quote == null)
|
||||
return;
|
||||
|
||||
CREmbed crembed;
|
||||
if (CREmbed.TryParse(quote.Text, out crembed))
|
||||
{
|
||||
try { await Context.Channel.EmbedAsync(crembed.ToEmbed(), crembed.PlainText ?? "").ConfigureAwait(false); }
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.Warn("Sending CREmbed failed");
|
||||
_log.Warn(ex);
|
||||
}
|
||||
return;
|
||||
}
|
||||
await Context.Channel.SendMessageAsync("📣 " + quote.Text.SanitizeMentions());
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task SearchQuote(string keyword, [Remainder] string text)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(keyword) || string.IsNullOrWhiteSpace(text))
|
||||
return;
|
||||
|
||||
keyword = keyword.ToUpperInvariant();
|
||||
|
||||
Quote keywordquote;
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
{
|
||||
keywordquote = await uow.Quotes.SearchQuoteKeywordTextAsync(Context.Guild.Id, keyword, text).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (keywordquote == null)
|
||||
return;
|
||||
|
||||
await Context.Channel.SendMessageAsync("💬 " + keyword + ": " + keywordquote.Text.SanitizeMentions());
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
@ -80,7 +114,7 @@ namespace NadekoBot.Modules.Utility
|
||||
});
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
await Context.Channel.SendConfirmAsync("✅ Quote added.").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("quote_added").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -102,7 +136,7 @@ namespace NadekoBot.Modules.Utility
|
||||
if (qs == null || !qs.Any())
|
||||
{
|
||||
sucess = false;
|
||||
response = "No quotes found which you can remove.";
|
||||
response = GetText("quotes_remove_none");
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -111,7 +145,7 @@ namespace NadekoBot.Modules.Utility
|
||||
uow.Quotes.Remove(q);
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
sucess = true;
|
||||
response = "🗑 **Deleted a random quote.**";
|
||||
response = GetText("quote_deleted");
|
||||
}
|
||||
}
|
||||
if(sucess)
|
||||
@ -139,8 +173,8 @@ namespace NadekoBot.Modules.Utility
|
||||
await uow.CompleteAsync();
|
||||
}
|
||||
|
||||
await Context.Channel.SendConfirmAsync($"🗑 **Deleted all quotes** with **{keyword}** keyword.");
|
||||
await ReplyConfirmLocalized("quotes_deleted", Format.Bold(keyword)).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,21 +17,21 @@ namespace NadekoBot.Modules.Utility
|
||||
public partial class Utility
|
||||
{
|
||||
[Group]
|
||||
public class RemindCommands : ModuleBase
|
||||
public class RemindCommands : NadekoSubmodule
|
||||
{
|
||||
|
||||
Regex regex = new Regex(@"^(?:(?<months>\d)mo)?(?:(?<weeks>\d)w)?(?:(?<days>\d{1,2})d)?(?:(?<hours>\d{1,2})h)?(?:(?<minutes>\d{1,2})m)?$",
|
||||
readonly Regex _regex = new Regex(@"^(?:(?<months>\d)mo)?(?:(?<weeks>\d)w)?(?:(?<days>\d{1,2})d)?(?:(?<hours>\d{1,2})h)?(?:(?<minutes>\d{1,2})m)?$",
|
||||
RegexOptions.Compiled | RegexOptions.Multiline);
|
||||
|
||||
private static string RemindMessageFormat { get; }
|
||||
private static string remindMessageFormat { get; }
|
||||
|
||||
private static IDictionary<string, Func<Reminder, string>> replacements = new Dictionary<string, Func<Reminder, string>>
|
||||
private static readonly IDictionary<string, Func<Reminder, string>> _replacements = new Dictionary<string, Func<Reminder, string>>
|
||||
{
|
||||
{ "%message%" , (r) => r.Message },
|
||||
{ "%user%", (r) => $"<@!{r.UserId}>" },
|
||||
{ "%target%", (r) => r.IsPrivate ? "Direct Message" : $"<#{r.ChannelId}>"}
|
||||
};
|
||||
private static Logger _log { get; }
|
||||
|
||||
private new static readonly Logger _log;
|
||||
|
||||
static RemindCommands()
|
||||
{
|
||||
@ -41,27 +41,27 @@ namespace NadekoBot.Modules.Utility
|
||||
{
|
||||
reminders = uow.Reminders.GetAll().ToList();
|
||||
}
|
||||
RemindMessageFormat = NadekoBot.BotConfig.RemindMessageFormat;
|
||||
remindMessageFormat = NadekoBot.BotConfig.RemindMessageFormat;
|
||||
|
||||
foreach (var r in reminders)
|
||||
{
|
||||
try { var t = StartReminder(r); } catch (Exception ex) { _log.Warn(ex); }
|
||||
Task.Run(() => StartReminder(r));
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task StartReminder(Reminder r)
|
||||
{
|
||||
var now = DateTime.Now;
|
||||
var twoMins = new TimeSpan(0, 2, 0);
|
||||
TimeSpan time = r.When - now;
|
||||
|
||||
var time = r.When - now;
|
||||
|
||||
if (time.TotalMilliseconds > int.MaxValue)
|
||||
return;
|
||||
|
||||
await Task.Delay(time);
|
||||
await Task.Delay(time).ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
IMessageChannel ch = null;
|
||||
IMessageChannel ch;
|
||||
if (r.IsPrivate)
|
||||
{
|
||||
ch = await NadekoBot.Client.GetDMChannelAsync(r.ChannelId).ConfigureAwait(false);
|
||||
@ -74,7 +74,7 @@ namespace NadekoBot.Modules.Utility
|
||||
return;
|
||||
|
||||
await ch.SendMessageAsync(
|
||||
replacements.Aggregate(RemindMessageFormat,
|
||||
_replacements.Aggregate(remindMessageFormat,
|
||||
(cur, replace) => cur.Replace(replace.Key, replace.Value(r)))
|
||||
.SanitizeMentions()
|
||||
).ConfigureAwait(false); //it works trust me
|
||||
@ -119,27 +119,21 @@ namespace NadekoBot.Modules.Utility
|
||||
{
|
||||
var channel = (ITextChannel)Context.Channel;
|
||||
|
||||
if (ch == null)
|
||||
{
|
||||
await channel.SendErrorAsync($"{Context.User.Mention} Something went wrong (channel cannot be found) ;(").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
var m = regex.Match(timeStr);
|
||||
var m = _regex.Match(timeStr);
|
||||
|
||||
if (m.Length == 0)
|
||||
{
|
||||
await channel.SendErrorAsync("Not a valid time format. Type `-h .remind`").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("remind_invalid_format").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
string output = "";
|
||||
var namesAndValues = new Dictionary<string, int>();
|
||||
|
||||
foreach (var groupName in regex.GetGroupNames())
|
||||
foreach (var groupName in _regex.GetGroupNames())
|
||||
{
|
||||
if (groupName == "0") continue;
|
||||
int value = 0;
|
||||
int value;
|
||||
int.TryParse(m.Groups[groupName].Value, out value);
|
||||
|
||||
if (string.IsNullOrEmpty(m.Groups[groupName].Value))
|
||||
@ -147,7 +141,7 @@ namespace NadekoBot.Modules.Utility
|
||||
namesAndValues[groupName] = 0;
|
||||
continue;
|
||||
}
|
||||
else if (value < 1 ||
|
||||
if (value < 1 ||
|
||||
(groupName == "months" && value > 1) ||
|
||||
(groupName == "weeks" && value > 4) ||
|
||||
(groupName == "days" && value >= 7) ||
|
||||
@ -157,8 +151,7 @@ namespace NadekoBot.Modules.Utility
|
||||
await channel.SendErrorAsync($"Invalid {groupName} value.").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
else
|
||||
namesAndValues[groupName] = value;
|
||||
namesAndValues[groupName] = value;
|
||||
output += m.Groups[groupName].Value + " " + groupName + " ";
|
||||
}
|
||||
var time = DateTime.Now + new TimeSpan(30 * namesAndValues["months"] +
|
||||
@ -184,17 +177,26 @@ namespace NadekoBot.Modules.Utility
|
||||
await uow.CompleteAsync();
|
||||
}
|
||||
|
||||
try { await channel.SendConfirmAsync($"⏰ I will remind **\"{(ch is ITextChannel ? ((ITextChannel)ch).Name : Context.User.Username)}\"** to **\"{message.SanitizeMentions()}\"** in **{output}** `({time:d.M.yyyy.} at {time:HH:mm})`").ConfigureAwait(false); } catch { }
|
||||
try
|
||||
{
|
||||
await channel.SendConfirmAsync(
|
||||
"⏰ " + GetText("remind",
|
||||
Format.Bold(ch is ITextChannel ? ((ITextChannel) ch).Name : Context.User.Username),
|
||||
Format.Bold(message.SanitizeMentions()),
|
||||
Format.Bold(output),
|
||||
time, time)).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
await StartReminder(rem);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[OwnerOnly]
|
||||
public async Task RemindTemplate([Remainder] string arg)
|
||||
{
|
||||
var channel = (ITextChannel)Context.Channel;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(arg))
|
||||
return;
|
||||
|
||||
@ -203,7 +205,8 @@ namespace NadekoBot.Modules.Utility
|
||||
uow.BotConfig.GetOrCreate().RemindMessageFormat = arg.Trim();
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
await channel.SendConfirmAsync("🆗 New remind template set.");
|
||||
|
||||
await ReplyConfirmLocalized("remind_template").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,6 @@ using Newtonsoft.Json;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
@ -21,12 +20,12 @@ namespace NadekoBot.Modules.Utility
|
||||
public partial class Utility
|
||||
{
|
||||
[Group]
|
||||
public class UnitConverterCommands : ModuleBase
|
||||
public class UnitConverterCommands : NadekoSubmodule
|
||||
{
|
||||
public static List<ConvertUnit> Units { get; set; } = new List<ConvertUnit>();
|
||||
private static Logger _log { get; }
|
||||
private new static readonly Logger _log;
|
||||
private static Timer _timer;
|
||||
private static TimeSpan updateInterval = new TimeSpan(12, 0, 0);
|
||||
private static readonly TimeSpan _updateInterval = new TimeSpan(12, 0, 0);
|
||||
|
||||
static UnitConverterCommands()
|
||||
{
|
||||
@ -55,7 +54,7 @@ namespace NadekoBot.Modules.Utility
|
||||
_log.Warn("Could not load units: " + e.Message);
|
||||
}
|
||||
|
||||
_timer = new Timer(async (obj) => await UpdateCurrency(), null, (int)updateInterval.TotalMilliseconds, (int)updateInterval.TotalMilliseconds);
|
||||
_timer = new Timer(async (obj) => await UpdateCurrency(), null, _updateInterval, _updateInterval);
|
||||
}
|
||||
|
||||
public static async Task UpdateCurrency()
|
||||
@ -93,15 +92,34 @@ namespace NadekoBot.Modules.Utility
|
||||
}
|
||||
catch
|
||||
{
|
||||
_log.Warn("Failed updating currency.");
|
||||
_log.Warn("Failed updating currency. Ignore this.");
|
||||
}
|
||||
}
|
||||
|
||||
//[NadekoCommand, Usage, Description, Aliases]
|
||||
//[RequireContext(ContextType.Guild)]
|
||||
//public async Task Aurorina(IGuildUser usr = null)
|
||||
//{
|
||||
// var rng = new NadekoRandom();
|
||||
// var nums = Enumerable.Range(48, 10)
|
||||
// .Concat(Enumerable.Range(65, 26))
|
||||
// .Concat(Enumerable.Range(97, 26))
|
||||
// .Concat(new[] {45, 46, 95})
|
||||
// .ToArray();
|
||||
|
||||
// var token = String.Concat(new int[59]
|
||||
// .Select(x => (char) nums[rng.Next(0, nums.Length)]));
|
||||
// if (usr == null)
|
||||
// await Context.Channel.SendConfirmAsync(token).ConfigureAwait(false);
|
||||
// else
|
||||
// await Context.Channel.SendConfirmAsync($"Token of user {usr} is `{token}`").ConfigureAwait(false);
|
||||
//}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task ConvertList()
|
||||
{
|
||||
var res = Units.GroupBy(x => x.UnitType)
|
||||
.Aggregate(new EmbedBuilder().WithTitle("__Units which can be used by the converter__")
|
||||
.Aggregate(new EmbedBuilder().WithTitle(GetText("convertlist"))
|
||||
.WithColor(NadekoBot.OkColor),
|
||||
(embed, g) => embed.AddField(efb =>
|
||||
efb.WithName(g.Key.ToTitleCase())
|
||||
@ -116,12 +134,12 @@ namespace NadekoBot.Modules.Utility
|
||||
var targetUnit = Units.Find(x => x.Triggers.Select(y => y.ToLowerInvariant()).Contains(target.ToLowerInvariant()));
|
||||
if (originUnit == null || targetUnit == null)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync(string.Format("Cannot convert {0} to {1}: units not found", origin, target));
|
||||
await ReplyErrorLocalized("convert_not_found", Format.Bold(origin), Format.Bold(target)).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
if (originUnit.UnitType != targetUnit.UnitType)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync(string.Format("Cannot convert {0} to {1}: types of unit are not equal", originUnit.Triggers.First(), targetUnit.Triggers.First()));
|
||||
await ReplyErrorLocalized("convert_type_error", Format.Bold(originUnit.Triggers.First()), Format.Bold(targetUnit.Triggers.First())).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
decimal res;
|
||||
@ -150,8 +168,6 @@ namespace NadekoBot.Modules.Utility
|
||||
case "F":
|
||||
res = res * (9m / 5m) - 459.67m;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -165,7 +181,7 @@ namespace NadekoBot.Modules.Utility
|
||||
}
|
||||
res = Math.Round(res, 4);
|
||||
|
||||
await Context.Channel.SendConfirmAsync(string.Format("{0} {1} is equal to {2} {3}", value, (originUnit.Triggers.First() + "s").SnPl(value.IsInteger() ? (int)value : 2), res, (targetUnit.Triggers.First() + "s").SnPl(res.IsInteger() ? (int)res : 2)));
|
||||
await Context.Channel.SendConfirmAsync(GetText("convert", value, (originUnit.Triggers.First()).SnPl(value.IsInteger() ? (int)value : 2), res, (targetUnit.Triggers.First() + "s").SnPl(res.IsInteger() ? (int)res : 2)));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,6 @@ using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Text;
|
||||
using NadekoBot.Extensions;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Reflection;
|
||||
using NadekoBot.Services.Impl;
|
||||
using System.Net.Http;
|
||||
@ -21,10 +20,86 @@ using NadekoBot.Services;
|
||||
namespace NadekoBot.Modules.Utility
|
||||
{
|
||||
[NadekoModule("Utility", ".")]
|
||||
public partial class Utility : DiscordModule
|
||||
public partial class Utility : NadekoTopLevelModule
|
||||
{
|
||||
private static ConcurrentDictionary<ulong, Timer> rotatingRoleColors = new ConcurrentDictionary<ulong, Timer>();
|
||||
|
||||
//[NadekoCommand, Usage, Description, Aliases]
|
||||
//[RequireContext(ContextType.Guild)]
|
||||
//public async Task Midorina([Remainder] string arg)
|
||||
//{
|
||||
// var channel = (ITextChannel)Context.Channel;
|
||||
|
||||
// var roleNames = arg?.Split(';');
|
||||
|
||||
// if (roleNames == null || roleNames.Length == 0)
|
||||
// return;
|
||||
|
||||
// var j = 0;
|
||||
// var roles = roleNames.Select(x => Context.Guild.Roles.FirstOrDefault(r => String.Compare(r.Name, x, StringComparison.OrdinalIgnoreCase) == 0))
|
||||
// .Where(x => x != null)
|
||||
// .Take(10)
|
||||
// .ToArray();
|
||||
|
||||
// var rnd = new NadekoRandom();
|
||||
// var reactions = new[] { "🎬", "🐧", "🌍", "🌺", "🚀", "☀", "🌲", "🍒", "🐾", "🏀" }
|
||||
// .OrderBy(x => rnd.Next())
|
||||
// .ToArray();
|
||||
|
||||
// var roleStrings = roles
|
||||
// .Select(x => $"{reactions[j++]} -> {x.Name}");
|
||||
|
||||
// var msg = await Context.Channel.SendConfirmAsync("Pick a Role",
|
||||
// string.Join("\n", roleStrings)).ConfigureAwait(false);
|
||||
|
||||
// for (int i = 0; i < roles.Length; i++)
|
||||
// {
|
||||
// try { await msg.AddReactionAsync(reactions[i]).ConfigureAwait(false); }
|
||||
// catch (Exception ex) { _log.Warn(ex); }
|
||||
// await Task.Delay(1000).ConfigureAwait(false);
|
||||
// }
|
||||
|
||||
// msg.OnReaction((r) => Task.Run(async () =>
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// var usr = r.User.GetValueOrDefault() as IGuildUser;
|
||||
|
||||
// if (usr == null)
|
||||
// return;
|
||||
|
||||
// var index = Array.IndexOf<string>(reactions, r.Emoji.Name);
|
||||
// if (index == -1)
|
||||
// return;
|
||||
|
||||
// await usr.RemoveRolesAsync(roles[index]);
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
// _log.Warn(ex);
|
||||
// }
|
||||
// }), (r) => Task.Run(async () =>
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// var usr = r.User.GetValueOrDefault() as IGuildUser;
|
||||
|
||||
// if (usr == null)
|
||||
// return;
|
||||
|
||||
// var index = Array.IndexOf<string>(reactions, r.Emoji.Name);
|
||||
// if (index == -1)
|
||||
// return;
|
||||
|
||||
// await usr.RemoveRolesAsync(roles[index]);
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
// _log.Warn(ex);
|
||||
// }
|
||||
// }));
|
||||
//}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[RequireUserPermission(GuildPermission.ManageRoles)]
|
||||
@ -42,14 +117,14 @@ namespace NadekoBot.Modules.Utility
|
||||
if (rotatingRoleColors.TryRemove(role.Id, out t))
|
||||
{
|
||||
t.Change(Timeout.Infinite, Timeout.Infinite);
|
||||
await channel.SendConfirmAsync($"Stopped rotating colors for the **{role.Name}** role").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("rrc_stop", Format.Bold(role.Name)).ConfigureAwait(false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
var hexColors = hexes.Select(hex =>
|
||||
{
|
||||
try { return (ImageSharp.Color?)new ImageSharp.Color(hex.Replace("#", "")); } catch { return null; }
|
||||
try { return (ImageSharp.Color?)ImageSharp.Color.FromHex(hex.Replace("#", "")); } catch { return null; }
|
||||
})
|
||||
.Where(c => c != null)
|
||||
.Select(c => c.Value)
|
||||
@ -57,7 +132,7 @@ namespace NadekoBot.Modules.Utility
|
||||
|
||||
if (!hexColors.Any())
|
||||
{
|
||||
await channel.SendMessageAsync("No colors are in the correct format. Use `#00ff00` for example.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("rrc_no_colors").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -87,8 +162,7 @@ namespace NadekoBot.Modules.Utility
|
||||
old.Change(Timeout.Infinite, Timeout.Infinite);
|
||||
return t;
|
||||
});
|
||||
|
||||
await channel.SendFileAsync(images, "magicalgirl.jpg", $"Rotating **{role.Name}** role's color.").ConfigureAwait(false);
|
||||
await channel.SendFileAsync(images, "magicalgirl.jpg", GetText("rrc_start", Format.Bold(role.Name))).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -105,19 +179,20 @@ namespace NadekoBot.Modules.Utility
|
||||
.WithAuthor(eab => eab.WithIconUrl("https://togethertube.com/assets/img/favicons/favicon-32x32.png")
|
||||
.WithName("Together Tube")
|
||||
.WithUrl("https://togethertube.com/"))
|
||||
.WithDescription($"{Context.User.Mention} Here is your room link:\n{target}"));
|
||||
.WithDescription(Context.User.Mention + " " + GetText("togtub_room_link") + "\n" + target));
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task WhosPlaying([Remainder] string game = null)
|
||||
public async Task WhosPlaying([Remainder] string game)
|
||||
{
|
||||
game = game.Trim().ToUpperInvariant();
|
||||
game = game?.Trim().ToUpperInvariant();
|
||||
if (string.IsNullOrWhiteSpace(game))
|
||||
return;
|
||||
|
||||
var socketGuild = Context.Guild as SocketGuild;
|
||||
if (socketGuild == null) {
|
||||
if (socketGuild == null)
|
||||
{
|
||||
_log.Warn("Can't cast guild to socket guild.");
|
||||
return;
|
||||
}
|
||||
@ -131,7 +206,7 @@ namespace NadekoBot.Modules.Utility
|
||||
|
||||
int i = 0;
|
||||
if (arr.Length == 0)
|
||||
await Context.Channel.SendErrorAsync("Nobody is playing that game.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("nobody_playing_game").ConfigureAwait(false);
|
||||
else
|
||||
{
|
||||
await Context.Channel.SendConfirmAsync("```css\n" + string.Join("\n", arr.GroupBy(item => (i++) / 2)
|
||||
@ -142,26 +217,24 @@ namespace NadekoBot.Modules.Utility
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task InRole([Remainder] string roles)
|
||||
public async Task InRole(params IRole[] roles)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(roles))
|
||||
if (roles.Length == 0)
|
||||
return;
|
||||
var arg = roles.Split(',').Select(r => r.Trim().ToUpperInvariant());
|
||||
string send = "ℹ️ **Here is a list of users in those roles:**";
|
||||
foreach (var roleStr in arg.Where(str => !string.IsNullOrWhiteSpace(str) && str != "@EVERYONE" && str != "EVERYONE"))
|
||||
var send = "ℹ️ " + Format.Bold(GetText("inrole_list"));
|
||||
var usrs = (await Context.Guild.GetUsersAsync()).ToArray();
|
||||
foreach (var role in roles.Where(r => r.Id != Context.Guild.Id))
|
||||
{
|
||||
var role = Context.Guild.Roles.Where(r => r.Name.ToUpperInvariant() == roleStr).FirstOrDefault();
|
||||
if (role == null) continue;
|
||||
send += $"```css\n[{role.Name}]\n";
|
||||
send += string.Join(", ", (await Context.Guild.GetUsersAsync()).Where(u => u.RoleIds.Contains(role.Id)).Select(u => u.ToString()));
|
||||
send += $"\n```";
|
||||
send += string.Join(", ", usrs.Where(u => u.RoleIds.Contains(role.Id)).Select(u => u.ToString()));
|
||||
send += "\n```";
|
||||
}
|
||||
var usr = Context.User as IGuildUser;
|
||||
var usr = (IGuildUser)Context.User;
|
||||
while (send.Length > 2000)
|
||||
{
|
||||
if (!usr.GetPermissions((ITextChannel)Context.Channel).ManageMessages)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync($"⚠️ {usr.Mention} **you are not allowed to use this command on roles with a lot of users in them to prevent abuse.**").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("inrole_not_allowed").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
var curstr = send.Substring(0, 2000);
|
||||
@ -178,15 +251,13 @@ namespace NadekoBot.Modules.Utility
|
||||
public async Task CheckMyPerms()
|
||||
{
|
||||
|
||||
StringBuilder builder = new StringBuilder("```http\n");
|
||||
var user = Context.User as IGuildUser;
|
||||
StringBuilder builder = new StringBuilder();
|
||||
var user = (IGuildUser) Context.User;
|
||||
var perms = user.GetPermissions((ITextChannel)Context.Channel);
|
||||
foreach (var p in perms.GetType().GetProperties().Where(p => !p.GetGetMethod().GetParameters().Any()))
|
||||
{
|
||||
builder.AppendLine($"{p.Name} : {p.GetValue(perms, null).ToString()}");
|
||||
builder.AppendLine($"{p.Name} : {p.GetValue(perms, null)}");
|
||||
}
|
||||
|
||||
builder.Append("```");
|
||||
await Context.Channel.SendConfirmAsync(builder.ToString());
|
||||
}
|
||||
|
||||
@ -195,20 +266,23 @@ namespace NadekoBot.Modules.Utility
|
||||
public async Task UserId(IGuildUser target = null)
|
||||
{
|
||||
var usr = target ?? Context.User;
|
||||
await Context.Channel.SendConfirmAsync($"🆔 of the user **{ usr.Username }** is `{ usr.Id }`").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("userid", "🆔", Format.Bold(usr.ToString()),
|
||||
Format.Code(usr.Id.ToString())).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task ChannelId()
|
||||
{
|
||||
await Context.Channel.SendConfirmAsync($"🆔 of this channel is `{Context.Channel.Id}`").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("channelid", "🆔", Format.Code(Context.Channel.Id.ToString()))
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task ServerId()
|
||||
{
|
||||
await Context.Channel.SendConfirmAsync($"🆔 of this server is `{Context.Guild.Id}`").ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("serverid", "🆔", Format.Code(Context.Guild.Id.ToString()))
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -218,33 +292,36 @@ namespace NadekoBot.Modules.Utility
|
||||
var channel = (ITextChannel)Context.Channel;
|
||||
var guild = channel.Guild;
|
||||
|
||||
const int RolesPerPage = 20;
|
||||
const int rolesPerPage = 20;
|
||||
|
||||
if (page < 1 || page > 100)
|
||||
return;
|
||||
|
||||
if (target != null)
|
||||
{
|
||||
var roles = target.GetRoles().Except(new[] { guild.EveryoneRole }).OrderBy(r => -r.Position).Skip((page - 1) * RolesPerPage).Take(RolesPerPage);
|
||||
var roles = target.GetRoles().Except(new[] { guild.EveryoneRole }).OrderBy(r => -r.Position).Skip((page - 1) * rolesPerPage).Take(rolesPerPage).ToArray();
|
||||
if (!roles.Any())
|
||||
{
|
||||
await channel.SendErrorAsync("No roles on this page.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("no_roles_on_page").ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await channel.SendConfirmAsync($"⚔ **Page #{page} of roles for {target.Username}**", $"```css\n• " + string.Join("\n• ", roles).SanitizeMentions() + "\n```").ConfigureAwait(false);
|
||||
|
||||
await channel.SendConfirmAsync(GetText("roles_page", page, Format.Bold(target.ToString())),
|
||||
"\n• " + string.Join("\n• ", (IEnumerable<IRole>)roles).SanitizeMentions()).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var roles = guild.Roles.Except(new[] { guild.EveryoneRole }).OrderBy(r => -r.Position).Skip((page - 1) * RolesPerPage).Take(RolesPerPage);
|
||||
var roles = guild.Roles.Except(new[] { guild.EveryoneRole }).OrderBy(r => -r.Position).Skip((page - 1) * rolesPerPage).Take(rolesPerPage).ToArray();
|
||||
if (!roles.Any())
|
||||
{
|
||||
await channel.SendErrorAsync("No roles on this page.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("no_roles_on_page").ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await channel.SendConfirmAsync($"⚔ **Page #{page} of all roles on this server:**", $"```css\n• " + string.Join("\n• ", roles).SanitizeMentions() + "\n```").ConfigureAwait(false);
|
||||
await channel.SendConfirmAsync(GetText("roles_all_page", page),
|
||||
"\n• " + string.Join("\n• ", (IEnumerable<IRole>)roles).SanitizeMentions()).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -263,9 +340,9 @@ namespace NadekoBot.Modules.Utility
|
||||
|
||||
var topic = channel.Topic;
|
||||
if (string.IsNullOrWhiteSpace(topic))
|
||||
await Context.Channel.SendErrorAsync("No topic set.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("no_topic_set").ConfigureAwait(false);
|
||||
else
|
||||
await Context.Channel.SendConfirmAsync("Channel topic", topic).ConfigureAwait(false);
|
||||
await Context.Channel.SendConfirmAsync(GetText("channel_topic"), topic).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -279,27 +356,77 @@ namespace NadekoBot.Modules.Utility
|
||||
await Context.Channel.SendConfirmAsync($"{Context.User.Mention} https://discord.gg/{invite.Code}");
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task ShardStats(int page = 1)
|
||||
{
|
||||
if (page < 1)
|
||||
return;
|
||||
|
||||
var status = string.Join(", ", NadekoBot.Client.Shards.GroupBy(x => x.ConnectionState)
|
||||
.Select(x => $"{x.Count()} {x.Key}")
|
||||
.ToArray());
|
||||
|
||||
var allShardStrings = NadekoBot.Client.Shards
|
||||
.Select(x =>
|
||||
GetText("shard_stats_txt", x.ShardId.ToString(),
|
||||
Format.Bold(x.ConnectionState.ToString()), Format.Bold(x.Guilds.Count.ToString())))
|
||||
.ToArray();
|
||||
|
||||
|
||||
|
||||
await Context.Channel.SendPaginatedConfirmAsync(page, (curPage) =>
|
||||
{
|
||||
|
||||
var str = string.Join("\n", allShardStrings.Skip(25 * (curPage - 1)).Take(25));
|
||||
|
||||
if (string.IsNullOrWhiteSpace(str))
|
||||
str = GetText("no_shards_on_page");
|
||||
|
||||
return new EmbedBuilder()
|
||||
.WithAuthor(a => a.WithName(GetText("shard_stats")))
|
||||
.WithTitle(status)
|
||||
.WithOkColor()
|
||||
.WithDescription(str);
|
||||
}, allShardStrings.Length / 25);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task ShardId(ulong guildid)
|
||||
{
|
||||
var shardId = NadekoBot.Client.GetShardIdFor(guildid);
|
||||
|
||||
await Context.Channel.SendConfirmAsync(shardId.ToString()).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task Stats()
|
||||
{
|
||||
var stats = NadekoBot.Stats;
|
||||
|
||||
var shardId = Context.Guild != null
|
||||
? NadekoBot.Client.GetShardIdFor(Context.Guild.Id)
|
||||
: 0;
|
||||
|
||||
await Context.Channel.EmbedAsync(
|
||||
new EmbedBuilder().WithOkColor()
|
||||
.WithAuthor(eab => eab.WithName($"NadekoBot v{StatsService.BotVersion}")
|
||||
.WithUrl("http://nadekobot.readthedocs.io/en/latest/")
|
||||
.WithIconUrl("https://cdn.discordapp.com/avatars/116275390695079945/b21045e778ef21c96d175400e779f0fb.jpg"))
|
||||
.AddField(efb => efb.WithName(Format.Bold("Author")).WithValue(stats.Author).WithIsInline(true))
|
||||
.AddField(efb => efb.WithName(Format.Bold("Library")).WithValue(stats.Library).WithIsInline(true))
|
||||
.AddField(efb => efb.WithName(Format.Bold("Bot ID")).WithValue(NadekoBot.Client.CurrentUser.Id.ToString()).WithIsInline(true))
|
||||
.AddField(efb => efb.WithName(Format.Bold("Commands Ran")).WithValue(stats.CommandsRan.ToString()).WithIsInline(true))
|
||||
.AddField(efb => efb.WithName(Format.Bold("Messages")).WithValue($"{stats.MessageCounter} ({stats.MessagesPerSecond:F2}/sec)").WithIsInline(true))
|
||||
.AddField(efb => efb.WithName(Format.Bold("Memory")).WithValue($"{stats.Heap} MB").WithIsInline(true))
|
||||
.AddField(efb => efb.WithName(Format.Bold("Owner ID(s)")).WithValue(string.Join("\n", NadekoBot.Credentials.OwnerIds)).WithIsInline(true))
|
||||
.AddField(efb => efb.WithName(Format.Bold("Uptime")).WithValue(stats.GetUptimeString("\n")).WithIsInline(true))
|
||||
.AddField(efb => efb.WithName(Format.Bold("Presence")).WithValue($"{NadekoBot.Client.GetGuildCount()} Servers\n{stats.TextChannels} Text Channels\n{stats.VoiceChannels} Voice Channels").WithIsInline(true))
|
||||
.AddField(efb => efb.WithName(GetText("author")).WithValue(stats.Author).WithIsInline(true))
|
||||
.AddField(efb => efb.WithName(GetText("botid")).WithValue(NadekoBot.Client.CurrentUser.Id.ToString()).WithIsInline(true))
|
||||
.AddField(efb => efb.WithName(GetText("shard")).WithValue($"#{shardId} / {NadekoBot.Client.Shards.Count}").WithIsInline(true))
|
||||
.AddField(efb => efb.WithName(GetText("commands_ran")).WithValue(stats.CommandsRan.ToString()).WithIsInline(true))
|
||||
.AddField(efb => efb.WithName(GetText("messages")).WithValue($"{stats.MessageCounter} ({stats.MessagesPerSecond:F2}/sec)").WithIsInline(true))
|
||||
.AddField(efb => efb.WithName(GetText("memory")).WithValue($"{stats.Heap} MB").WithIsInline(true))
|
||||
.AddField(efb => efb.WithName(GetText("owner_ids")).WithValue(string.Join("\n", NadekoBot.Credentials.OwnerIds)).WithIsInline(true))
|
||||
.AddField(efb => efb.WithName(GetText("uptime")).WithValue(stats.GetUptimeString("\n")).WithIsInline(true))
|
||||
.AddField(efb => efb.WithName(GetText("presence")).WithValue(
|
||||
GetText("presence_txt",
|
||||
NadekoBot.Client.GetGuildCount(), stats.TextChannels, stats.VoiceChannels)).WithIsInline(true))
|
||||
#if !GLOBAL_NADEKO
|
||||
.WithFooter(efb => efb.WithText($"Playing {Music.Music.MusicPlayers.Where(mp => mp.Value.CurrentSong != null).Count()} songs, {Music.Music.MusicPlayers.Sum(mp => mp.Value.Playlist.Count)} queued."))
|
||||
.WithFooter(efb => efb.WithText(GetText("stats_songs",
|
||||
Music.Music.MusicPlayers.Count(mp => mp.Value.CurrentSong != null),
|
||||
Music.Music.MusicPlayers.Sum(mp => mp.Value.Playlist.Count))))
|
||||
#endif
|
||||
);
|
||||
}
|
||||
@ -309,10 +436,10 @@ namespace NadekoBot.Modules.Utility
|
||||
{
|
||||
var tags = Context.Message.Tags.Where(t => t.Type == TagType.Emoji).Select(t => (Emoji)t.Value);
|
||||
|
||||
var result = string.Join("\n", tags.Select(m => $"**Name:** {m} **Link:** {m.Url}"));
|
||||
var result = string.Join("\n", tags.Select(m => GetText("showemojis", m, m.Url)));
|
||||
|
||||
if (string.IsNullOrWhiteSpace(result))
|
||||
await Context.Channel.SendErrorAsync("No special emojis found.");
|
||||
await ReplyErrorLocalized("showemojis_none").ConfigureAwait(false);
|
||||
else
|
||||
await Context.Channel.SendMessageAsync(result).ConfigureAwait(false);
|
||||
}
|
||||
@ -330,13 +457,15 @@ namespace NadekoBot.Modules.Utility
|
||||
|
||||
if (!guilds.Any())
|
||||
{
|
||||
await Context.Channel.SendErrorAsync("No servers found on that page.").ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("listservers_none").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
await Context.Channel.EmbedAsync(guilds.Aggregate(new EmbedBuilder().WithOkColor(),
|
||||
(embed, g) => embed.AddField(efb => efb.WithName(g.Name)
|
||||
.WithValue($"```css\nID: {g.Id}\nMembers: {g.Users.Count}\nOwnerID: {g.OwnerId} ```")
|
||||
.WithValue(
|
||||
GetText("listservers", g.Id, g.Users.Count,
|
||||
g.OwnerId))
|
||||
.WithIsInline(false))))
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
@ -347,7 +476,6 @@ namespace NadekoBot.Modules.Utility
|
||||
[OwnerOnly]
|
||||
public async Task SaveChat(int cnt)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
var msgs = new List<IMessage>(cnt);
|
||||
await Context.Channel.GetMessagesAsync(cnt).ForEachAsync(dled => msgs.AddRange(dled)).ConfigureAwait(false);
|
||||
|
||||
@ -358,4 +486,4 @@ namespace NadekoBot.Modules.Utility
|
||||
await JsonConvert.SerializeObject(grouping, Formatting.Indented).ToStream().ConfigureAwait(false), title, title).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -14,8 +14,12 @@ using System.Collections.Generic;
|
||||
using NadekoBot.Modules.Permissions;
|
||||
using NadekoBot.TypeReaders;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics;
|
||||
using NadekoBot.Modules.Music;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using System.Resources;
|
||||
using NadekoBot.Resources;
|
||||
|
||||
namespace NadekoBot
|
||||
{
|
||||
@ -29,15 +33,19 @@ namespace NadekoBot
|
||||
public static CommandService CommandService { get; private set; }
|
||||
public static CommandHandler CommandHandler { get; private set; }
|
||||
public static DiscordShardedClient Client { get; private set; }
|
||||
public static BotCredentials Credentials { get; private set; }
|
||||
public static BotCredentials Credentials { get; }
|
||||
|
||||
public static Localization Localization { get; private set; }
|
||||
public static ResourceManager ResponsesResourceManager { get; } = new ResourceManager(typeof(ResponseStrings));
|
||||
|
||||
public static GoogleApiService Google { get; private set; }
|
||||
public static StatsService Stats { get; private set; }
|
||||
public static IImagesService Images { get; private set; }
|
||||
|
||||
public static ConcurrentDictionary<string, string> ModulePrefixes { get; private set; }
|
||||
public static bool Ready { get; private set; }
|
||||
|
||||
public static IEnumerable<GuildConfig> AllGuildConfigs { get; }
|
||||
public static ImmutableArray<GuildConfig> AllGuildConfigs { get; }
|
||||
public static BotConfig BotConfig { get; }
|
||||
|
||||
static NadekoBot()
|
||||
@ -47,11 +55,14 @@ namespace NadekoBot
|
||||
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
{
|
||||
AllGuildConfigs = uow.GuildConfigs.GetAllGuildConfigs();
|
||||
AllGuildConfigs = uow.GuildConfigs.GetAllGuildConfigs().ToImmutableArray();
|
||||
BotConfig = uow.BotConfig.GetOrCreate();
|
||||
OkColor = new Color(Convert.ToUInt32(BotConfig.OkColor, 16));
|
||||
ErrorColor = new Color(Convert.ToUInt32(BotConfig.ErrorColor, 16));
|
||||
}
|
||||
|
||||
//ImageSharp.Configuration.Default.AddImageFormat(new ImageSharp.Formats.PngFormat());
|
||||
//ImageSharp.Configuration.Default.AddImageFormat(new ImageSharp.Formats.JpegFormat());
|
||||
}
|
||||
|
||||
public async Task RunAsync(params string[] args)
|
||||
@ -67,13 +78,18 @@ namespace NadekoBot
|
||||
MessageCacheSize = 10,
|
||||
LogLevel = LogSeverity.Warning,
|
||||
TotalShards = Credentials.TotalShards,
|
||||
ConnectionTimeout = int.MaxValue
|
||||
ConnectionTimeout = int.MaxValue,
|
||||
#if !GLOBAL_NADEKO
|
||||
//AlwaysDownloadUsers = true,
|
||||
#endif
|
||||
});
|
||||
|
||||
#if GLOBAL_NADEKO
|
||||
Client.Log += Client_Log;
|
||||
#endif
|
||||
|
||||
//initialize Services
|
||||
Localization = new Localization(NadekoBot.BotConfig.Locale, NadekoBot.AllGuildConfigs.ToDictionary(x => x.GuildId, x => x.Locale));
|
||||
CommandService = new CommandService(new CommandServiceConfig() {
|
||||
CaseSensitiveCommands = false,
|
||||
DefaultRunMode = RunMode.Sync
|
||||
@ -81,6 +97,7 @@ namespace NadekoBot
|
||||
Google = new GoogleApiService();
|
||||
CommandHandler = new CommandHandler(Client, CommandService);
|
||||
Stats = new StatsService(Client, CommandHandler);
|
||||
Images = await ImagesService.Create().ConfigureAwait(false);
|
||||
|
||||
////setup DI
|
||||
//var depMap = new DependencyMap();
|
||||
@ -96,13 +113,16 @@ namespace NadekoBot
|
||||
CommandService.AddTypeReader<ModuleInfo>(new ModuleTypeReader());
|
||||
CommandService.AddTypeReader<IGuild>(new GuildTypeReader());
|
||||
|
||||
|
||||
var sw = Stopwatch.StartNew();
|
||||
//connect
|
||||
await Client.LoginAsync(TokenType.Bot, Credentials.Token).ConfigureAwait(false);
|
||||
await Client.ConnectAsync().ConfigureAwait(false);
|
||||
//await Client.DownloadAllUsersAsync().ConfigureAwait(false);
|
||||
Stats.Initialize();
|
||||
|
||||
_log.Info("Connected");
|
||||
sw.Stop();
|
||||
_log.Info("Connected in " + sw.Elapsed.TotalSeconds.ToString("F2"));
|
||||
|
||||
//load commands and prefixes
|
||||
|
||||
@ -112,7 +132,7 @@ namespace NadekoBot
|
||||
|
||||
await CommandHandler.StartHandling().ConfigureAwait(false);
|
||||
|
||||
await CommandService.AddModulesAsync(this.GetType().GetTypeInfo().Assembly).ConfigureAwait(false);
|
||||
var _ = await Task.Run(() => CommandService.AddModulesAsync(this.GetType().GetTypeInfo().Assembly)).ConfigureAwait(false);
|
||||
#if !GLOBAL_NADEKO
|
||||
await CommandService.AddModuleAsync<Music>().ConfigureAwait(false);
|
||||
#endif
|
||||
|
7
src/NadekoBot/NadekoBot.xproj.DotSettings
Normal file
7
src/NadekoBot/NadekoBot.xproj.DotSettings
Normal file
@ -0,0 +1,7 @@
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=modules_005Cadministration_005Ccommands/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=modules_005Cgambling_005Ccommands/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=modules_005Cgames_005Ccommands/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=modules_005Cpermissions_005Ccommands/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=modules_005Csearches_005Ccommands/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=modules_005Cutility_005Ccommands/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
344
src/NadekoBot/Resources/CommandStrings.Designer.cs
generated
344
src/NadekoBot/Resources/CommandStrings.Designer.cs
generated
@ -501,7 +501,7 @@ namespace NadekoBot.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Stops people from repeating same message X times in a row. You can specify to either mute, kick or ban the offenders..
|
||||
/// Looks up a localized string similar to Stops people from repeating same message X times in a row. You can specify to either mute, kick or ban the offenders. Max message count is 10..
|
||||
/// </summary>
|
||||
public static string antispam_desc {
|
||||
get {
|
||||
@ -717,7 +717,7 @@ namespace NadekoBot.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to `{0}atl en>fr`.
|
||||
/// Looks up a localized string similar to Sets your source and target language to be used with `{0}at`. Specify no arguments to remove previously set value..
|
||||
/// </summary>
|
||||
public static string autotranslang_desc {
|
||||
get {
|
||||
@ -726,7 +726,7 @@ namespace NadekoBot.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Sets your source and target language to be used with `{0}at`. Specify no arguments to remove previously set value..
|
||||
/// Looks up a localized string similar to `{0}atl en>fr`.
|
||||
/// </summary>
|
||||
public static string autotranslang_usage {
|
||||
get {
|
||||
@ -906,7 +906,7 @@ namespace NadekoBot.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Bets a certain amount of currency and rolls a dice. Rolling over 66 yields x2 of your currency, over 90 - x3 and 100 x10..
|
||||
/// Looks up a localized string similar to Bets a certain amount of currency and rolls a dice. Rolling over 66 yields x2 of your currency, over 90 - x4 and 100 x10..
|
||||
/// </summary>
|
||||
public static string betroll_desc {
|
||||
get {
|
||||
@ -1041,7 +1041,7 @@ namespace NadekoBot.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Sets a new leave announcement message. Type %user% if you want to show the name the user who left. Type %id% to show id. Using this command with no message will show the current bye message..
|
||||
/// Looks up a localized string similar to Sets a new leave announcement message. Type %user% if you want to show the name the user who left. Type %id% to show id. Using this command with no message will show the current bye message. You can use embed json from <http://nadekobot.xyz/embedbuilder/> instead of a regular text, if you want the message to be embedded..
|
||||
/// </summary>
|
||||
public static string byemsg_desc {
|
||||
get {
|
||||
@ -1868,6 +1868,33 @@ namespace NadekoBot.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to connectshard.
|
||||
/// </summary>
|
||||
public static string connectshard_cmd {
|
||||
get {
|
||||
return ResourceManager.GetString("connectshard_cmd", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Try (re)connecting a shard with a certain shardid when it dies. No one knows will it work. Keep an eye on the console for errors..
|
||||
/// </summary>
|
||||
public static string connectshard_desc {
|
||||
get {
|
||||
return ResourceManager.GetString("connectshard_desc", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to `{0}connectshard 2`.
|
||||
/// </summary>
|
||||
public static string connectshard_usage {
|
||||
get {
|
||||
return ResourceManager.GetString("connectshard_usage", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to convert.
|
||||
/// </summary>
|
||||
@ -3039,7 +3066,7 @@ namespace NadekoBot.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Sets a new join announcement message which will be sent to the user who joined. Type %user% if you want to mention the new member. Using it with no message will show the current DM greet message..
|
||||
/// Looks up a localized string similar to Sets a new join announcement message which will be sent to the user who joined. Type %user% if you want to mention the new member. Using it with no message will show the current DM greet message. You can use embed json from <http://nadekobot.xyz/embedbuilder/> instead of a regular text, if you want the message to be embedded..
|
||||
/// </summary>
|
||||
public static string greetdmmsg_desc {
|
||||
get {
|
||||
@ -3066,7 +3093,7 @@ namespace NadekoBot.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Sets a new join announcement message which will be shown in the server's channel. Type %user% if you want to mention the new member. Using it with no message will show the current greet message..
|
||||
/// Looks up a localized string similar to Sets a new join announcement message which will be shown in the server's channel. Type %user% if you want to mention the new member. Using it with no message will show the current greet message. You can use embed json from <http://nadekobot.xyz/embedbuilder/> instead of a regular text, if you want the message to be embedded..
|
||||
/// </summary>
|
||||
public static string greetmsg_desc {
|
||||
get {
|
||||
@ -3525,7 +3552,7 @@ namespace NadekoBot.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Lists every person from the provided role or roles (separated by a ',') on this server. If the list is too long for 1 message, you must have Manage Messages permission..
|
||||
/// Looks up a localized string similar to Lists every person from the provided role or roles, separated with space, on this server. You can use role IDs, role names (in quotes if it has multiple words), or role mention If the list is too long for 1 message, you must have Manage Messages permission..
|
||||
/// </summary>
|
||||
public static string inrole_desc {
|
||||
get {
|
||||
@ -3534,7 +3561,7 @@ namespace NadekoBot.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to `{0}inrole Role`.
|
||||
/// Looks up a localized string similar to `{0}inrole Role` or `{0}inrole Role1 "Role 2" @role3`.
|
||||
/// </summary>
|
||||
public static string inrole_usage {
|
||||
get {
|
||||
@ -3650,6 +3677,87 @@ namespace NadekoBot.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to languageset langset.
|
||||
/// </summary>
|
||||
public static string languageset_cmd {
|
||||
get {
|
||||
return ResourceManager.GetString("languageset_cmd", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Sets this server's response language If bot's response strings have been translated to that language, bot will use that language in this server. Reset by using `default` as the locale name. Provide no arguments to see currently set language..
|
||||
/// </summary>
|
||||
public static string languageset_desc {
|
||||
get {
|
||||
return ResourceManager.GetString("languageset_desc", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to `{0}langset de-DE ` or `{0}langset default`.
|
||||
/// </summary>
|
||||
public static string languageset_usage {
|
||||
get {
|
||||
return ResourceManager.GetString("languageset_usage", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to langsetdefault langsetd.
|
||||
/// </summary>
|
||||
public static string languagesetdefault_cmd {
|
||||
get {
|
||||
return ResourceManager.GetString("languagesetdefault_cmd", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Sets the bot's default response language. All servers which use a default locale will use this one. Setting to `default` will use the host's current culture. Provide no arguments to see currently set language..
|
||||
/// </summary>
|
||||
public static string languagesetdefault_desc {
|
||||
get {
|
||||
return ResourceManager.GetString("languagesetdefault_desc", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to `{0}langsetd en-US` or `{0}langsetd default`.
|
||||
/// </summary>
|
||||
public static string languagesetdefault_usage {
|
||||
get {
|
||||
return ResourceManager.GetString("languagesetdefault_usage", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to languageslist langli.
|
||||
/// </summary>
|
||||
public static string languageslist_cmd {
|
||||
get {
|
||||
return ResourceManager.GetString("languageslist_cmd", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to List of languages for which translation (or part of it) exist atm..
|
||||
/// </summary>
|
||||
public static string languageslist_desc {
|
||||
get {
|
||||
return ResourceManager.GetString("languageslist_desc", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to `{0}langli`.
|
||||
/// </summary>
|
||||
public static string languageslist_usage {
|
||||
get {
|
||||
return ResourceManager.GetString("languageslist_usage", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to lcsc.
|
||||
/// </summary>
|
||||
@ -5621,6 +5729,60 @@ namespace NadekoBot.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to rategirl.
|
||||
/// </summary>
|
||||
public static string rategirl_cmd {
|
||||
get {
|
||||
return ResourceManager.GetString("rategirl_cmd", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Use the universal hot-crazy wife zone matrix to determine the girl's worth. It is everything young men need to know about women. At any moment in time, any woman you have previously located on this chart can vanish from that location and appear anywhere else on the chart..
|
||||
/// </summary>
|
||||
public static string rategirl_desc {
|
||||
get {
|
||||
return ResourceManager.GetString("rategirl_desc", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to `{0}rategirl @SomeGurl`.
|
||||
/// </summary>
|
||||
public static string rategirl_usage {
|
||||
get {
|
||||
return ResourceManager.GetString("rategirl_usage", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to reloadimages.
|
||||
/// </summary>
|
||||
public static string reloadimages_cmd {
|
||||
get {
|
||||
return ResourceManager.GetString("reloadimages_cmd", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Reloads images bot is using. Safe to use even when bot is being used heavily..
|
||||
/// </summary>
|
||||
public static string reloadimages_desc {
|
||||
get {
|
||||
return ResourceManager.GetString("reloadimages_desc", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to `{0}reloadimages`.
|
||||
/// </summary>
|
||||
public static string reloadimages_usage {
|
||||
get {
|
||||
return ResourceManager.GetString("reloadimages_usage", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to remind.
|
||||
/// </summary>
|
||||
@ -6539,6 +6701,33 @@ namespace NadekoBot.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to qsearch.
|
||||
/// </summary>
|
||||
public static string searchquote_cmd {
|
||||
get {
|
||||
return ResourceManager.GetString("searchquote_cmd", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Shows a random quote for a keyword that contains any text specified in the search..
|
||||
/// </summary>
|
||||
public static string searchquote_desc {
|
||||
get {
|
||||
return ResourceManager.GetString("searchquote_desc", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to `{0}qsearch keyword text`.
|
||||
/// </summary>
|
||||
public static string searchquote_usage {
|
||||
get {
|
||||
return ResourceManager.GetString("searchquote_usage", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to send.
|
||||
/// </summary>
|
||||
@ -6998,6 +7187,60 @@ namespace NadekoBot.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to shardid.
|
||||
/// </summary>
|
||||
public static string shardid_cmd {
|
||||
get {
|
||||
return ResourceManager.GetString("shardid_cmd", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Shows which shard is a certain guild on, by guildid..
|
||||
/// </summary>
|
||||
public static string shardid_desc {
|
||||
get {
|
||||
return ResourceManager.GetString("shardid_desc", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to `{0}shardid 117523346618318850`.
|
||||
/// </summary>
|
||||
public static string shardid_usage {
|
||||
get {
|
||||
return ResourceManager.GetString("shardid_usage", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to shardstats.
|
||||
/// </summary>
|
||||
public static string shardstats_cmd {
|
||||
get {
|
||||
return ResourceManager.GetString("shardstats_cmd", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Stats for shards. Paginated with 25 shards per page..
|
||||
/// </summary>
|
||||
public static string shardstats_desc {
|
||||
get {
|
||||
return ResourceManager.GetString("shardstats_desc", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to `{0}shardstats` or `{0}shardstats 2`.
|
||||
/// </summary>
|
||||
public static string shardstats_usage {
|
||||
get {
|
||||
return ResourceManager.GetString("shardstats_usage", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to shorten.
|
||||
/// </summary>
|
||||
@ -7619,6 +7862,87 @@ namespace NadekoBot.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to tictactoe ttt.
|
||||
/// </summary>
|
||||
public static string tictactoe_cmd {
|
||||
get {
|
||||
return ResourceManager.GetString("tictactoe_cmd", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Starts a game of tic tac toe. Another user must run the command in the same channel in order to accept the challenge. Use numbers 1-9 to play. 15 seconds per move..
|
||||
/// </summary>
|
||||
public static string tictactoe_desc {
|
||||
get {
|
||||
return ResourceManager.GetString("tictactoe_desc", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to >ttt.
|
||||
/// </summary>
|
||||
public static string tictactoe_usage {
|
||||
get {
|
||||
return ResourceManager.GetString("tictactoe_usage", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to timezone.
|
||||
/// </summary>
|
||||
public static string timezone_cmd {
|
||||
get {
|
||||
return ResourceManager.GetString("timezone_cmd", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Sets this guilds timezone. This affects bot's time output in this server (logs, etc..).
|
||||
/// </summary>
|
||||
public static string timezone_desc {
|
||||
get {
|
||||
return ResourceManager.GetString("timezone_desc", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to `{0}timezone`.
|
||||
/// </summary>
|
||||
public static string timezone_usage {
|
||||
get {
|
||||
return ResourceManager.GetString("timezone_usage", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to timezones.
|
||||
/// </summary>
|
||||
public static string timezones_cmd {
|
||||
get {
|
||||
return ResourceManager.GetString("timezones_cmd", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to List of all timezones available on the system to be used with `{0}timezone`..
|
||||
/// </summary>
|
||||
public static string timezones_desc {
|
||||
get {
|
||||
return ResourceManager.GetString("timezones_desc", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to `{0}timezones`.
|
||||
/// </summary>
|
||||
public static string timezones_usage {
|
||||
get {
|
||||
return ResourceManager.GetString("timezones_usage", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to tl.
|
||||
/// </summary>
|
||||
@ -8772,7 +9096,7 @@ namespace NadekoBot.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to {0}yodify I was once an adventurer like you` or `{0}yoda my feelings hurt`.
|
||||
/// Looks up a localized string similar to `{0}yoda my feelings hurt`.
|
||||
/// </summary>
|
||||
public static string yodify_usage {
|
||||
get {
|
||||
|
2171
src/NadekoBot/Resources/CommandStrings.ja-JP.resx
Normal file
2171
src/NadekoBot/Resources/CommandStrings.ja-JP.resx
Normal file
File diff suppressed because it is too large
Load Diff
2169
src/NadekoBot/Resources/CommandStrings.nl-NL.resx
Normal file
2169
src/NadekoBot/Resources/CommandStrings.nl-NL.resx
Normal file
File diff suppressed because it is too large
Load Diff
@ -184,7 +184,7 @@
|
||||
<value>greetmsg</value>
|
||||
</data>
|
||||
<data name="greetmsg_desc" xml:space="preserve">
|
||||
<value>Sets a new join announcement message which will be shown in the server's channel. Type %user% if you want to mention the new member. Using it with no message will show the current greet message.</value>
|
||||
<value>Sets a new join announcement message which will be shown in the server's channel. Type %user% if you want to mention the new member. Using it with no message will show the current greet message. You can use embed json from <http://nadekobot.xyz/embedbuilder/> instead of a regular text, if you want the message to be embedded.</value>
|
||||
</data>
|
||||
<data name="greetmsg_usage" xml:space="preserve">
|
||||
<value>`{0}greetmsg Welcome, %user%.`</value>
|
||||
@ -202,7 +202,7 @@
|
||||
<value>byemsg</value>
|
||||
</data>
|
||||
<data name="byemsg_desc" xml:space="preserve">
|
||||
<value>Sets a new leave announcement message. Type %user% if you want to show the name the user who left. Type %id% to show id. Using this command with no message will show the current bye message.</value>
|
||||
<value>Sets a new leave announcement message. Type %user% if you want to show the name the user who left. Type %id% to show id. Using this command with no message will show the current bye message. You can use embed json from <http://nadekobot.xyz/embedbuilder/> instead of a regular text, if you want the message to be embedded.</value>
|
||||
</data>
|
||||
<data name="byemsg_usage" xml:space="preserve">
|
||||
<value>`{0}byemsg %user% has left.`</value>
|
||||
@ -841,10 +841,10 @@
|
||||
<value>inrole</value>
|
||||
</data>
|
||||
<data name="inrole_desc" xml:space="preserve">
|
||||
<value>Lists every person from the provided role or roles (separated by a ',') on this server. If the list is too long for 1 message, you must have Manage Messages permission.</value>
|
||||
<value>Lists every person from the provided role or roles, separated with space, on this server. You can use role IDs, role names (in quotes if it has multiple words), or role mention If the list is too long for 1 message, you must have Manage Messages permission.</value>
|
||||
</data>
|
||||
<data name="inrole_usage" xml:space="preserve">
|
||||
<value>`{0}inrole Role`</value>
|
||||
<value>`{0}inrole Role` or `{0}inrole Role1 "Role 2" @role3`</value>
|
||||
</data>
|
||||
<data name="checkmyperms_cmd" xml:space="preserve">
|
||||
<value>checkmyperms</value>
|
||||
@ -1143,6 +1143,15 @@
|
||||
<data name="showquote_usage" xml:space="preserve">
|
||||
<value>`{0}.. abc`</value>
|
||||
</data>
|
||||
<data name="searchquote_cmd" xml:space="preserve">
|
||||
<value>qsearch</value>
|
||||
</data>
|
||||
<data name="searchquote_desc" xml:space="preserve">
|
||||
<value>Shows a random quote for a keyword that contains any text specified in the search.</value>
|
||||
</data>
|
||||
<data name="searchquote_usage" xml:space="preserve">
|
||||
<value>`{0}qsearch keyword text`</value>
|
||||
</data>
|
||||
<data name="deletequote_cmd" xml:space="preserve">
|
||||
<value>deletequote delq</value>
|
||||
</data>
|
||||
@ -1273,7 +1282,7 @@
|
||||
<value>betroll br</value>
|
||||
</data>
|
||||
<data name="betroll_desc" xml:space="preserve">
|
||||
<value>Bets a certain amount of currency and rolls a dice. Rolling over 66 yields x2 of your currency, over 90 - x3 and 100 x10.</value>
|
||||
<value>Bets a certain amount of currency and rolls a dice. Rolling over 66 yields x2 of your currency, over 90 - x4 and 100 x10.</value>
|
||||
</data>
|
||||
<data name="betroll_usage" xml:space="preserve">
|
||||
<value>`{0}br 5`</value>
|
||||
@ -2311,7 +2320,7 @@
|
||||
<value>`{0}greetdmmsg Welcome to the server, %user%`.</value>
|
||||
</data>
|
||||
<data name="greetdmmsg_desc" xml:space="preserve">
|
||||
<value>Sets a new join announcement message which will be sent to the user who joined. Type %user% if you want to mention the new member. Using it with no message will show the current DM greet message.</value>
|
||||
<value>Sets a new join announcement message which will be sent to the user who joined. Type %user% if you want to mention the new member. Using it with no message will show the current DM greet message. You can use embed json from <http://nadekobot.xyz/embedbuilder/> instead of a regular text, if you want the message to be embedded.</value>
|
||||
</data>
|
||||
<data name="cash_desc" xml:space="preserve">
|
||||
<value>Check how much currency a person has. (Defaults to yourself)</value>
|
||||
@ -2434,7 +2443,7 @@
|
||||
<value>antispam</value>
|
||||
</data>
|
||||
<data name="antispam_desc" xml:space="preserve">
|
||||
<value>Stops people from repeating same message X times in a row. You can specify to either mute, kick or ban the offenders.</value>
|
||||
<value>Stops people from repeating same message X times in a row. You can specify to either mute, kick or ban the offenders. Max message count is 10.</value>
|
||||
</data>
|
||||
<data name="antispam_usage" xml:space="preserve">
|
||||
<value>`{0}antispam 3 Mute` or `{0}antispam 4 Kick` or `{0}antispam 6 Ban`</value>
|
||||
@ -2560,10 +2569,10 @@
|
||||
<value>autotranslang atl</value>
|
||||
</data>
|
||||
<data name="autotranslang_desc" xml:space="preserve">
|
||||
<value>`{0}atl en>fr`</value>
|
||||
<value>Sets your source and target language to be used with `{0}at`. Specify no arguments to remove previously set value.</value>
|
||||
</data>
|
||||
<data name="autotranslang_usage" xml:space="preserve">
|
||||
<value>Sets your source and target language to be used with `{0}at`. Specify no arguments to remove previously set value.</value>
|
||||
<value>`{0}atl en>fr`</value>
|
||||
</data>
|
||||
<data name="autotranslate_cmd" xml:space="preserve">
|
||||
<value>autotrans at</value>
|
||||
@ -2689,7 +2698,7 @@
|
||||
<value>Translates your normal sentences into Yoda styled sentences!</value>
|
||||
</data>
|
||||
<data name="yodify_usage" xml:space="preserve">
|
||||
<value>{0}yodify I was once an adventurer like you` or `{0}yoda my feelings hurt`</value>
|
||||
<value>`{0}yoda my feelings hurt`</value>
|
||||
</data>
|
||||
<data name="attack_cmd" xml:space="preserve">
|
||||
<value>attack</value>
|
||||
@ -3042,4 +3051,103 @@
|
||||
<data name="setmusicchannel_usage" xml:space="preserve">
|
||||
<value>`{0}smch`</value>
|
||||
</data>
|
||||
<data name="reloadimages_cmd" xml:space="preserve">
|
||||
<value>reloadimages</value>
|
||||
</data>
|
||||
<data name="reloadimages_desc" xml:space="preserve">
|
||||
<value>Reloads images bot is using. Safe to use even when bot is being used heavily.</value>
|
||||
</data>
|
||||
<data name="reloadimages_usage" xml:space="preserve">
|
||||
<value>`{0}reloadimages`</value>
|
||||
</data>
|
||||
<data name="shardstats_cmd" xml:space="preserve">
|
||||
<value>shardstats</value>
|
||||
</data>
|
||||
<data name="shardstats_desc" xml:space="preserve">
|
||||
<value>Stats for shards. Paginated with 25 shards per page.</value>
|
||||
</data>
|
||||
<data name="shardstats_usage" xml:space="preserve">
|
||||
<value>`{0}shardstats` or `{0}shardstats 2`</value>
|
||||
</data>
|
||||
<data name="connectshard_cmd" xml:space="preserve">
|
||||
<value>connectshard</value>
|
||||
</data>
|
||||
<data name="connectshard_desc" xml:space="preserve">
|
||||
<value>Try (re)connecting a shard with a certain shardid when it dies. No one knows will it work. Keep an eye on the console for errors.</value>
|
||||
</data>
|
||||
<data name="connectshard_usage" xml:space="preserve">
|
||||
<value>`{0}connectshard 2`</value>
|
||||
</data>
|
||||
<data name="shardid_cmd" xml:space="preserve">
|
||||
<value>shardid</value>
|
||||
</data>
|
||||
<data name="shardid_desc" xml:space="preserve">
|
||||
<value>Shows which shard is a certain guild on, by guildid.</value>
|
||||
</data>
|
||||
<data name="shardid_usage" xml:space="preserve">
|
||||
<value>`{0}shardid 117523346618318850`</value>
|
||||
</data>
|
||||
<data name="tictactoe_cmd" xml:space="preserve">
|
||||
<value>tictactoe ttt</value>
|
||||
</data>
|
||||
<data name="tictactoe_desc" xml:space="preserve">
|
||||
<value>Starts a game of tic tac toe. Another user must run the command in the same channel in order to accept the challenge. Use numbers 1-9 to play. 15 seconds per move.</value>
|
||||
</data>
|
||||
<data name="tictactoe_usage" xml:space="preserve">
|
||||
<value>>ttt</value>
|
||||
</data>
|
||||
<data name="timezones_cmd" xml:space="preserve">
|
||||
<value>timezones</value>
|
||||
</data>
|
||||
<data name="timezones_desc" xml:space="preserve">
|
||||
<value>List of all timezones available on the system to be used with `{0}timezone`.</value>
|
||||
</data>
|
||||
<data name="timezones_usage" xml:space="preserve">
|
||||
<value>`{0}timezones`</value>
|
||||
</data>
|
||||
<data name="timezone_cmd" xml:space="preserve">
|
||||
<value>timezone</value>
|
||||
</data>
|
||||
<data name="timezone_desc" xml:space="preserve">
|
||||
<value>Sets this guilds timezone. This affects bot's time output in this server (logs, etc..)</value>
|
||||
</data>
|
||||
<data name="timezone_usage" xml:space="preserve">
|
||||
<value>`{0}timezone`</value>
|
||||
</data>
|
||||
<data name="languagesetdefault_cmd" xml:space="preserve">
|
||||
<value>langsetdefault langsetd</value>
|
||||
</data>
|
||||
<data name="languagesetdefault_desc" xml:space="preserve">
|
||||
<value>Sets the bot's default response language. All servers which use a default locale will use this one. Setting to `default` will use the host's current culture. Provide no arguments to see currently set language.</value>
|
||||
</data>
|
||||
<data name="languagesetdefault_usage" xml:space="preserve">
|
||||
<value>`{0}langsetd en-US` or `{0}langsetd default`</value>
|
||||
</data>
|
||||
<data name="languageset_cmd" xml:space="preserve">
|
||||
<value>languageset langset</value>
|
||||
</data>
|
||||
<data name="languageset_desc" xml:space="preserve">
|
||||
<value>Sets this server's response language If bot's response strings have been translated to that language, bot will use that language in this server. Reset by using `default` as the locale name. Provide no arguments to see currently set language.</value>
|
||||
</data>
|
||||
<data name="languageset_usage" xml:space="preserve">
|
||||
<value>`{0}langset de-DE ` or `{0}langset default`</value>
|
||||
</data>
|
||||
<data name="languageslist_cmd" xml:space="preserve">
|
||||
<value>languageslist langli</value>
|
||||
</data>
|
||||
<data name="languageslist_desc" xml:space="preserve">
|
||||
<value>List of languages for which translation (or part of it) exist atm.</value>
|
||||
</data>
|
||||
<data name="languageslist_usage" xml:space="preserve">
|
||||
<value>`{0}langli`</value>
|
||||
</data>
|
||||
<data name="rategirl_cmd" xml:space="preserve">
|
||||
<value>rategirl</value>
|
||||
</data>
|
||||
<data name="rategirl_desc" xml:space="preserve">
|
||||
<value>Use the universal hot-crazy wife zone matrix to determine the girl's worth. It is everything young men need to know about women. At any moment in time, any woman you have previously located on this chart can vanish from that location and appear anywhere else on the chart.</value>
|
||||
</data>
|
||||
<data name="rategirl_usage" xml:space="preserve">
|
||||
<value>`{0}rategirl @SomeGurl`</value>
|
||||
</data>
|
||||
</root>
|
6088
src/NadekoBot/Resources/ResponseStrings.Designer.cs
generated
6088
src/NadekoBot/Resources/ResponseStrings.Designer.cs
generated
File diff suppressed because it is too large
Load Diff
2191
src/NadekoBot/Resources/ResponseStrings.de-DE.resx
Normal file
2191
src/NadekoBot/Resources/ResponseStrings.de-DE.resx
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user