Merge pull request #929 from Kwoth/dev

1.0.0 woop
This commit is contained in:
Master Kwoth 2016-12-28 20:15:34 +01:00 committed by GitHub
commit cd9a7cfdbf
131 changed files with 17446 additions and 26606 deletions

20
Dockerfile Normal file
View File

@ -0,0 +1,20 @@
FROM microsoft/dotnet:sdk
MAINTAINER Poag <poag@gany.net>
WORKDIR /opt/
#Install required software
RUN echo "deb http://www.deb-multimedia.org jessie main non-free" | tee /etc/apt/sources.list.d/debian-backports.list \
&& apt-get update \
&& apt-get install -y --force-yes deb-multimedia-keyring \
&& apt-get update \
&& apt-get install -y git libopus0 opus-tools libopus-dev libsodium-dev ffmpeg
#Download and install stable version of Nadeko
RUN curl -L https://github.com/Kwoth/NadekoBot-BashScript/raw/master/nadeko_installer_latest.sh | sh \
&& curl -L https://github.com/Kwoth/NadekoBot-BashScript/raw/master/nadeko_autorestart.sh > nadeko.sh \
&& chmod 755 nadeko.sh
VOLUME ["/opt"]
CMD ["/opt/nadeko.sh"]

View File

@ -1,14 +1,14 @@
![img](https://ci.appveyor.com/api/projects/status/gmu6b3ltc80hr3k9?svg=true) ![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/0ehQwTK2RBjAxzEY)
[![Documentation Status](https://readthedocs.org/projects/nadekobot/badge/?version=latest)](http://nadekobot.readthedocs.io/en/1.0/?badge=latest) [![Documentation Status](https://readthedocs.org/projects/nadekobot/badge/?version=latest)](http://nadekobot.readthedocs.io/en/latest/?badge=latest)
# NadekoBot # NadekoBot
[![nadeko1](https://cdn.discordapp.com/attachments/155726317222887425/252095170676391936/A1.jpg)](https://discordapp.com/oauth2/authorize?client_id=170254782546575360&scope=bot&permissions=66186303) [![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/1.0/Commands%20List/) [![nadeko2](https://cdn.discordapp.com/attachments/155726317222887425/252095207514832896/A2.jpg)](http://nadekobot.readthedocs.io/en/latest/Commands%20List/)
##For Update, Help and Guidlines ##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.` `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/1.0/) [![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/)

@ -1 +1 @@
Subproject commit 48b66d55f3f26f60fc001123ff6d7afeade3a51d Subproject commit 508026d5d4f4d8780d983c63ab25e4c15ad69e59

View File

@ -10,6 +10,7 @@ You can support the project on patreon: <https://patreon.com/nadekobot> or paypa
- [Music](#music) - [Music](#music)
- [NSFW](#nsfw) - [NSFW](#nsfw)
- [Permissions](#permissions) - [Permissions](#permissions)
- [Pokemon](#pokemon)
- [Searches](#searches) - [Searches](#searches)
- [Utility](#utility) - [Utility](#utility)
@ -28,13 +29,6 @@ Command and aliases | Description | Usage
`.ban` `.b` | Bans a user by ID or name with an optional message. **Requires BanMembers server permission.** | `.b "@some Guy" Your behaviour is toxic.` `.ban` `.b` | Bans a user by ID or name with an optional message. **Requires BanMembers server permission.** | `.b "@some Guy" Your behaviour is toxic.`
`.softban` `.sb` | Bans and then unbans a user by ID or name with an optional message. **Requires KickMembers server permission.** **Requires ManageMessages server permission.** | `.sb "@some Guy" Your behaviour is toxic.` `.softban` `.sb` | Bans and then unbans a user by ID or name with an optional message. **Requires KickMembers server permission.** **Requires ManageMessages server permission.** | `.sb "@some Guy" Your behaviour is toxic.`
`.kick` `.k` | Kicks a mentioned user. **Requires KickMembers server permission.** | `.k "@some Guy" Your behaviour is toxic.` `.kick` `.k` | Kicks a mentioned user. **Requires KickMembers server permission.** | `.k "@some Guy" Your behaviour is toxic.`
`.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`
`.deafen` `.deaf` | Deafens mentioned user or users. **Requires DeafenMembers server permission.** | `.deaf "@Someguy"` or `.deaf "@Someguy" "@Someguy"` `.deafen` `.deaf` | Deafens mentioned user or users. **Requires DeafenMembers server permission.** | `.deaf "@Someguy"` or `.deaf "@Someguy" "@Someguy"`
`.undeafen` `.undef` | Undeafens mentioned user or users. **Requires DeafenMembers server permission.** | `.undef "@Someguy"` or `.undef "@Someguy" "@Someguy"` `.undeafen` `.undef` | Undeafens mentioned user or users. **Requires DeafenMembers server permission.** | `.undef "@Someguy"` or `.undef "@Someguy" "@Someguy"`
`.delvoichanl` `.dvch` | Deletes a voice channel with a given name. **Requires ManageChannels server permission.** | `.dvch VoiceChannelName` `.delvoichanl` `.dvch` | Deletes a voice channel with a given name. **Requires ManageChannels server permission.** | `.dvch VoiceChannelName`
@ -63,13 +57,20 @@ Command and aliases | Description | Usage
`.lcsc` | Leaves Cross server channel instance from this channel. **Requires ManageServer server permission.** | `.lcsc` `.lcsc` | Leaves Cross server channel instance from this channel. **Requires ManageServer server permission.** | `.lcsc`
`.fwmsgs` | Toggles forwarding of non-command messages sent to bot's DM to the bot owners **Bot Owner only.** | `.fwmsgs` `.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` `.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` | Logs server activity in this channel. **Requires Administrator server permission.** **Bot Owner only.** | `.logserver` `.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` `.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`
`.userpresence` | Starts logging to this channel when someone from the server goes online/offline/idle. **Requires Administrator server permission.** | `.userpresence` `.logevents` | Shows a list of all events you can subscribe to with `.log` **Requires Administrator server permission.** **Bot Owner only.** | `.logevents`
`.voicepresence` | Toggles logging to this channel whenever someone joins or leaves a voice channel you are currently in. **Requires Administrator server permission.** | `.voicepresence` `.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 lit of all events you can subscribe to. **Requires Administrator server permission.** **Bot Owner only.** | `.log userpresence` or `.log userbanned`
`.repeatinvoke` `.repinv` | Immediately shows the repeat message and restarts the timer. **Requires ManageMessages server permission.** | `.repinv` `.repeatinvoke` `.repinv` | Immediately shows the repeat message and restarts the timer. **Requires ManageMessages server permission.** | `.repinv`
`.repeat` | Repeat a message every X minutes. If no parameters are specified, repeat is disabled. **Requires ManageMessages server permission.** | `.repeat 5 Hello there` `.repeat` | Repeat a message every X minutes. If no parameters are specified, repeat is disabled. **Requires ManageMessages server permission.** | `.repeat 5 Hello there`
`.migratedata` | Migrate data from old bot configuration **Bot Owner only.** | `.migratedata` `.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` `.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` `.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` `.listplaying` `.lipl` | Lists all playing statuses with their corresponding number. **Bot Owner only.** | `.lipl`
@ -89,7 +90,7 @@ Command and aliases | Description | Usage
`.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` `.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%`. `.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` `.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 mention the new member. Using it with no message will show the current bye message. **Requires ManageServer server permission.** | `.byemsg %user% has left.` `.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` `.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.** | `.voice+text` `.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` `.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`
@ -119,6 +120,8 @@ Command and aliases | Description | Usage
`.listcustreactg` `.lcrg` | Lists global or server custom reactions (20 commands per page) grouped by trigger, and show a number of responses for each. Running the command in DM will list global custom reactions, while running it in server will list that server's custom reactions. | `.lcrg 1` `.listcustreactg` `.lcrg` | Lists global or server custom reactions (20 commands per page) grouped by trigger, and show a number of responses for each. Running the command in DM will list global custom reactions, while running it in server will list that server's custom reactions. | `.lcrg 1`
`.showcustreact` `.scr` | Shows a custom reaction's response on a given ID. | `.scr 1` `.showcustreact` `.scr` | Shows a custom reaction's response on a given ID. | `.scr 1`
`.delcustreact` `.dcr` | Deletes a custom reaction on a specific index. If ran in DM, it is bot owner only and deletes a global custom reaction. If ran in a server, it requires Administration priviledges and removes server custom reaction. | `.dcr 5` `.delcustreact` `.dcr` | Deletes a custom reaction on a specific index. If ran in DM, it is bot owner only and deletes a global custom reaction. If ran in a server, it requires Administration priviledges and removes server custom reaction. | `.dcr 5`
`.crstatsclear` | Resets the counters on `.crstats`. You can specify a trigger to clear stats only for that trigger. | `.crstatsclear` or `.crstatsclear rng`
`.crstats` | Shows a list of custom reactions and the number of times they have been executed. Paginated with 10 per page. Use `.crstatsclear` to reset the counters. | `.crstats` or `.crstats 3`
###### [Back to TOC](#table-of-contents) ###### [Back to TOC](#table-of-contents)
@ -134,7 +137,7 @@ Command and aliases | Description | Usage
`$leaderboard` `$lb` | Displays bot currency leaderboard. | `$lb` `$leaderboard` `$lb` | Displays bot currency leaderboard. | `$lb`
`$race` | Starts a new animal race. | `$race` `$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` `$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`
`$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. | `$roll` or `$roll 7` or `$roll 3d5` `$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` `$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` `$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` `$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`
@ -155,8 +158,11 @@ Command and aliases | Description | Usage
`>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` `>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` `>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`
`>pollend` | Stops active poll on this server and prints the results in this channel. **Requires ManageMessages server permission.** | `>pollend` `>pollend` | Stops active poll on this server and prints the results in this channel. **Requires ManageMessages server permission.** | `>pollend`
`>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` `>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`
`>pick` | Picks the currency planted in this channel. | `>pick` `>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 a unit of currency to plant it in this channel. (If bot is restarted or crashes, the currency will be lost) | `>plant` `>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` `>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`
`>typestart` | Starts a typing contest. | `>typestart` `>typestart` | Starts a typing contest. | `>typestart`
@ -189,6 +195,7 @@ Command and aliases | Description | Usage
`!!stop` `!!s` | Stops the music and clears the playlist. Stays in the channel. | `!!s` `!!stop` `!!s` | Stops the music and clears the playlist. Stays in the channel. | `!!s`
`!!destroy` `!!d` | Completely stops the music and unbinds the bot from the channel. (may cause weird behaviour) | `!!d` `!!destroy` `!!d` | Completely stops the music and unbinds the bot from the channel. (may cause weird behaviour) | `!!d`
`!!pause` `!!p` | Pauses or Unpauses the song. | `!!p` `!!pause` `!!p` | Pauses or Unpauses the song. | `!!p`
`!!fairplay` `!!fp` | Toggles fairplay. While enabled, music player will prioritize songs from users who didn't have their song recently played instead of the song's position in the queue. | `!!fp`
`!!queue` `!!q` `!!yq` | Queue a song using keywords or a link. Bot will join your voice channel.**You must be in a voice channel**. | `!!q Dream Of Venice` `!!queue` `!!q` `!!yq` | Queue a song using keywords or a link. Bot will join your voice channel.**You must be in a voice channel**. | `!!q Dream Of Venice`
`!!soundcloudqueue` `!!sq` | Queue a soundcloud song using keywords. Bot will join your voice channel.**You must be in a voice channel**. | `!!sq Dream Of Venice` `!!soundcloudqueue` `!!sq` | Queue a soundcloud song using keywords. Bot will join your voice channel.**You must be in a voice channel**. | `!!sq Dream Of Venice`
`!!listqueue` `!!lq` | Lists 15 currently queued songs per page. Default page is 1. | `!!lq` or `!!lq 2` `!!listqueue` `!!lq` | Lists 15 currently queued songs per page. Default page is 1. | `!!lq` or `!!lq 2`
@ -205,14 +212,14 @@ Command and aliases | Description | Usage
`!!remove` `!!rm` | Remove a song by its # in the queue, or 'all' to remove whole queue. | `!!rm 5` `!!remove` `!!rm` | Remove a song by its # in the queue, or 'all' to remove whole queue. | `!!rm 5`
`!!movesong` `!!ms` | Moves a song from one position to another. | `!!ms 5>3` `!!movesong` `!!ms` | Moves a song from one position to another. | `!!ms 5>3`
`!!setmaxqueue` `!!smq` | Sets a maximum queue size. Supply 0 or no argument to have no limit. | `!!smq 50` or `!!smq` `!!setmaxqueue` `!!smq` | Sets a maximum queue size. Supply 0 or no argument to have no limit. | `!!smq 50` or `!!smq`
`!!setmaxplaytime` `!!smp` | Sets a maximum number of seconds (>14) a song can run before being skipped automatically. Set 0 to have no limit. | `!!smp 0` or `!!smp 270`
`!!reptcursong` `!!rcs` | Toggles repeat of current song. | `!!rcs` `!!reptcursong` `!!rcs` | Toggles repeat of current song. | `!!rcs`
`!!rpeatplaylst` `!!rpl` | Toggles repeat of all songs in the queue (every song that finishes is added to the end of the queue). | `!!rpl` `!!rpeatplaylst` `!!rpl` | Toggles repeat of all songs in the queue (every song that finishes is added to the end of the queue). | `!!rpl`
`!!save` | Saves a playlist under a certain name. Name must be no longer than 20 characters and mustn't contain dashes. | `!!save classical1` `!!save` | Saves a playlist under a certain name. Name must be no longer than 20 characters and mustn't contain dashes. | `!!save classical1`
`!!load` | Loads a playlist under a certain name. | `!!load classical-1` `!!load` | Loads a saved playlist using it's ID. Use `!!pls` to list all saved playlists and !!save to save new ones. | `!!load 5`
`!!playlists` `!!pls` | Lists all playlists. Paginated. 20 per page. Default page is 0. | `!!pls 1` `!!playlists` `!!pls` | Lists all playlists. Paginated. 20 per page. Default page is 0. | `!!pls 1`
`!!deleteplaylist` `!!delpls` | Deletes a saved playlist. Only if you made it or if you are the bot owner. | `!!delpls animu-5` `!!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` `!!goto` | Goes to a specific time in seconds in a song. | `!!goto 30`
`!!getlink` `!!gl` | Shows a link to the song in the queue by index, or the currently playing song by default. | `!!gl`
`!!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` `!!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`
###### [Back to TOC](#table-of-contents) ###### [Back to TOC](#table-of-contents)
@ -222,8 +229,8 @@ 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` `~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`
`~hentaibomb` | Shows a total 5 images (from gelbooru, danbooru, konachan, yandere and atfbooru). Tag is optional but preferred. | `~hentaibomb yuri` `~hentaibomb` | Shows a total 5 images (from gelbooru, danbooru, konachan, yandere and atfbooru). Tag is optional but preferred. | `~hentaibomb yuri`
`~yandere` | Shows a random image from yandere with a given tag. Tag is optional but preferred. (multiple tags are appended with +) | `~yandere tag1+tag2`
`~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` `~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` `~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` `~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` `~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`
@ -243,13 +250,13 @@ Command and aliases | Description | Usage
`;removeperm` `;rp` | Removes a permission from a given position in Permissions list. | `;rp 1` `;removeperm` `;rp` | Removes a permission from a given position in Permissions list. | `;rp 1`
`;moveperm` `;mp` | Moves permission from one position to another in Permissions list. | `;mp 2 4` `;moveperm` `;mp` | Moves permission from one position to another in Permissions list. | `;mp 2 4`
`;srvrcmd` `;sc` | Sets a command's permission at the server level. | `;sc "command name" disable` `;srvrcmd` `;sc` | Sets a command's permission at the server level. | `;sc "command name" disable`
`;srvrmdl` `;sm` | Sets a module's permission at the server level. | `;sm "module name" enable` `;srvrmdl` `;sm` | Sets a module's permission at the server level. | `;sm ModuleName enable`
`;usrcmd` `;uc` | Sets a command's permission at the user level. | `;uc "command name" enable SomeUsername` `;usrcmd` `;uc` | Sets a command's permission at the user level. | `;uc "command name" enable SomeUsername`
`;usrmdl` `;um` | Sets a module's permission at the user level. | `;um "module name" enable SomeUsername` `;usrmdl` `;um` | Sets a module's permission at the user level. | `;um ModuleName enable SomeUsername`
`;rolecmd` `;rc` | Sets a command's permission at the role level. | `;rc "command name" disable MyRole` `;rolecmd` `;rc` | Sets a command's permission at the role level. | `;rc "command name" disable MyRole`
`;rolemdl` `;rm` | Sets a module's permission at the role level. | `;rm "module name" enable MyRole` `;rolemdl` `;rm` | Sets a module's permission at the role level. | `;rm ModuleName enable MyRole`
`;chnlcmd` `;cc` | Sets a command's permission at the channel level. | `;cc "command name" enable SomeChannel` `;chnlcmd` `;cc` | Sets a command's permission at the channel level. | `;cc "command name" enable SomeChannel`
`;chnlmdl` `;cm` | Sets a module's permission at the channel level. | `;cm "module name" enable SomeChannel` `;chnlmdl` `;cm` | Sets a module's permission at the channel level. | `;cm ModuleName enable SomeChannel`
`;allchnlmdls` `;acm` | Enable or disable all modules in a specified channel. | `;acm enable #SomeChannel` `;allchnlmdls` `;acm` | Enable or disable all modules in a specified channel. | `;acm enable #SomeChannel`
`;allrolemdls` `;arm` | Enable or disable all modules for a specific role. | `;arm [enable/disable] MyRole` `;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` `;allusrmdls` `;aum` | Enable or disable all modules for a specific user. | `;aum enable @someone`
@ -268,10 +275,21 @@ Command and aliases | Description | Usage
###### [Back to TOC](#table-of-contents) ###### [Back to TOC](#table-of-contents)
### Pokemon
Command and aliases | Description | Usage
----------------|--------------|-------
`>attack` | Attacks a target with the given move. Use `>movelist` to see a list of moves your type can use. | `>attack "vine whip" @someguy`
`>movelist` `>ml` | Lists the moves you are able to use | `>ml`
`>heal` | Heals someone. Revives those who fainted. Costs a NadekoFlower | `>heal @someone`
`>type` | Get the poketype of the target. | `>type @someone`
`>settype` | Set your poketype. Costs a NadekoFlower. Provide no arguments to see a list of available types. | `>settype fire` or `>settype`
###### [Back to TOC](#table-of-contents)
### Searches ### Searches
Command and aliases | Description | Usage Command and aliases | Description | Usage
----------------|--------------|------- ----------------|--------------|-------
`~weather` `~we` | Shows weather data for a specified city and a country. BOTH ARE REQUIRED. Use country abbrevations. | `~we Moscow RF` `~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` `~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` `~imdb` `~omdb` | Queries omdb for movies or series, show first result. | `~imdb Batman vs Superman`
`~randomcat` `~meow` | Shows a random cat image. | `~meow` `~randomcat` `~meow` | Shows a random cat image. | `~meow`
@ -283,7 +301,9 @@ Command and aliases | Description | Usage
`~google` `~g` | Get a google search link for some terms. | `~google query` `~google` `~g` | Get a google search link for some terms. | `~google query`
`~magicthegathering` `~mtg` | Searches for a Magic The Gathering card. | `~magicthegathering about face` or `~mtg about face` `~magicthegathering` `~mtg` | Searches for a Magic The Gathering card. | `~magicthegathering about face` or `~mtg about face`
`~hearthstone` `~hs` | Searches for a Hearthstone card and shows its image. Takes a while to complete. | `~hs Ysera` `~hearthstone` `~hs` | Searches for a Hearthstone card and shows its image. Takes a while to complete. | `~hs Ysera`
`~yodify` `~yoda` | Translates your normal sentences into Yoda styled sentences! | ~yodify I was once an adventurer like you` or `~yoda my feelings hurt`
`~urbandict` `~ud` | Searches Urban Dictionary for a word. | `~ud Pineapple` `~urbandict` `~ud` | Searches Urban Dictionary for a word. | `~ud Pineapple`
`~define` `~def` | Finds a definition of a word. | `~def heresy`
`~#` | Searches Tagdef.com for a hashtag. | `~# ff` `~#` | Searches Tagdef.com for a hashtag. | `~# ff`
`~catfact` | Shows a random catfact from <http://catfacts-api.appspot.com/api/facts> | `~catfact` `~catfact` | Shows a random catfact from <http://catfacts-api.appspot.com/api/facts> | `~catfact`
`~revav` | Returns a google reverse image search for someone's avatar. | `~revav "@SomeGuy"` `~revav` | Returns a google reverse image search for someone's avatar. | `~revav "@SomeGuy"`
@ -293,8 +313,6 @@ Command and aliases | Description | Usage
`~color` `~clr` | Shows you what color corresponds to that hex. | `~clr 00ff00` `~color` `~clr` | Shows you what color corresponds to that hex. | `~clr 00ff00`
`~videocall` | Creates a private <http://www.appear.in> video call link for you and other mentioned people. The link is sent to mentioned people via a private message. | `~videocall "@SomeGuy"` `~videocall` | Creates a private <http://www.appear.in> video call link for you and other mentioned people. The link is sent to mentioned people via a private message. | `~videocall "@SomeGuy"`
`~avatar` `~av` | Shows a mentioned person's avatar. | `~av "@SomeGuy"` `~avatar` `~av` | Shows a mentioned person's avatar. | `~av "@SomeGuy"`
`~bfonline` `~bfo` | Gives you online players for BF3 and BF4 | `~bfo bf3` or `~bfo bf4`
`~bfuser` `~bfu` | Gives you back a battlefield user's stats. | `~bfu platform game user`
`~wikia` | Gives you back a wikia link | `~wikia mtg Vigilance` or `~wikia mlp Dashy` `~wikia` | Gives you back a wikia link | `~wikia mtg Vigilance` or `~wikia mlp Dashy`
`~minecraftping` `~mcping` | Pings a minecraft server. | `~mcping 127.0.0.1:25565` `~minecraftping` `~mcping` | Pings a minecraft server. | `~mcping 127.0.0.1:25565`
`~minecraftquery` `~mcq` | Finds information about a minecraft server. | `~mcq server:ip` `~minecraftquery` `~mcq` | Finds information about a minecraft server. | `~mcq server:ip`
@ -311,6 +329,7 @@ Command and aliases | Description | Usage
`~osu` | Shows osu stats for a player. | `~osu Name` or `~osu Name taiko` `~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` `~osub` | Shows information about an osu beatmap. | `~osub https://osu.ppy.sh/s/127712`
`~osu5` | Displays a user's top 5 plays. | `~osu5 Name` `~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` `~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` `~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` `~pokemon` `~poke` | Searches for a pokemon. | `~poke Sylveon`
@ -319,7 +338,7 @@ Command and aliases | Description | Usage
`~twitch` `~tw` | Notifies this channel when a certain user starts streaming. **Requires ManageMessages server permission.** | `~twitch 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` `~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` `~liststreams` `~ls` | Lists all streams you are following on this server. | `~ls`
`~removestream` `~rms` | Removes notifications of a certain streamer on this channel. **Requires ManageMessages server permission.** | `~rms SomeGuy` `~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` `~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` `~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` `~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`
@ -332,6 +351,7 @@ Command and aliases | Description | Usage
### Utility ### Utility
Command and aliases | Description | Usage Command and aliases | Description | Usage
----------------|--------------|------- ----------------|--------------|-------
`.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` `.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` `.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`
`.checkmyperms` | Checks your user-specific permissions on this channel. | `.checkmyperms` `.checkmyperms` | Checks your user-specific permissions on this channel. | `.checkmyperms`
@ -345,7 +365,6 @@ Command and aliases | Description | Usage
`.listservers` | Lists servers the bot is on with some basic info. 15 per page. **Bot Owner only.** | `.listservers 3` `.listservers` | Lists servers the bot is on with some basic info. 15 per page. **Bot Owner only.** | `.listservers 3`
`.calculate` `.calc` | Evaluate a mathematical expression. | `.calc 1+1` `.calculate` `.calc` | Evaluate a mathematical expression. | `.calc 1+1`
`.calcops` | Shows all available operations in .calc command | `.calcops` `.calcops` | Shows all available operations in .calc command | `.calcops`
`.togethertube` `.totube` | Creates a new room on <https://togethertube.com> and shows the link in the chat. | `.totube`
`.serverinfo` `.sinfo` | Shows info about the server the bot is on. If no channel is supplied, it defaults to current one. | `.sinfo Some Server` `.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` `.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` `.userinfo` `.uinfo` | Shows info about the user. If no user is supplied, it defaults a user running the command. | `.uinfo @SomeUser`
@ -354,7 +373,7 @@ Command and aliases | Description | Usage
`..` | Adds a new quote with the specified name and message. | `.. sayhi Hi` `..` | 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` `.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` `.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 Start now!` `.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%!` `.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` `.convertlist` | List of the convertible dimensions and currencies. | `.convertlist`
`.convert` | Convert quantities. Use `.convertlist` to see supported dimensions and currencies. | `.convert m km 1000` `.convert` | Convert quantities. Use `.convertlist` to see supported dimensions and currencies. | `.convert m km 1000`

View File

@ -27,6 +27,14 @@ There's no special requirement for the formatting of the response, so we could j
Now, if that command was ran in a server, anyone on that server can make the bot mention them, saying `It sure is, @Username` anytime they say "Nice Weather". If the command is ran in a direct message with the bot, then the custom reaction can be used on every server the bot is connected to. Now, if that command was ran in a server, anyone on that server can make the bot mention them, saying `It sure is, @Username` anytime they say "Nice Weather". If the command is ran in a direct message with the bot, then the custom reaction can be used on every server the bot is connected to.
###Block global Custom Reactions
If you want to disable some global custom reactions which you do not like, and you do not want to remove them or you are not the bot owner you can do so by adding a new Custom Reaction with the same trigger on your server, and set the response to `-`.
For example:
`.acr /o/ -`
Now if you try to trigger `/o/`, it won't print anything.
###Placeholders! ###Placeholders!
There are currently three different placeholders which we will look at, with more placeholders potentially coming in the future. There are currently three different placeholders which we will look at, with more placeholders potentially coming in the future.
@ -34,7 +42,8 @@ There are currently three different placeholders which we will look at, with mor
|:-----------:|-------------|---------------|-------| |:-----------:|-------------|---------------|-------|
|`%mention`|The `%mention%` placeholder is triggered when you type `@BotName` - It's important to note that if you've given the bot a custom nickname, this trigger won't work!|```.acr "Hello %mention%" I, %mention%, also say hello!```|Input: "Hello @BotName" Output: "I, @BotName, also say hello!"| |`%mention`|The `%mention%` placeholder is triggered when you type `@BotName` - It's important to note that if you've given the bot a custom nickname, this trigger won't work!|```.acr "Hello %mention%" I, %mention%, also say hello!```|Input: "Hello @BotName" Output: "I, @BotName, also say hello!"|
|`%user%`|The `%user%` placeholder mentions the person who said the command|`.acr "Who am I?" You are %user%!`|Input: "Who am I?" Output: "You are @Username!"| |`%user%`|The `%user%` placeholder mentions the person who said the command|`.acr "Who am I?" You are %user%!`|Input: "Who am I?" Output: "You are @Username!"|
|`%rng%`|The `%rng%` placeholder generates a random number between 0 and 10|`.acr "Random number" %rng%`|Input: "Random number" Output: "2"| |`%rng%`|The `%rng%` placeholder generates a random number between 0 and 10. You can also specify a custom range (%rng1-100%) even with negative numbers: `%rng-9--1%` (from -9 to -1) . |`.acr "Random number" %rng%`|Input: "Random number" Output: "2"|
|`%rnduser%`|The `%rnduser%` placeholder mentions a random user from the server. |`.acr "Random user" %rnduser%`|Input: "Random number" Output: @SomeUser|
|`%target%`|The `%target%` placeholder is used to make Nadeko Mention another person or phrase, it is only supported as part of the response|`.acr "Say this: " %target%`|Input: "Say this: I, @BotName, am a parrot!". Output: "I, @BotName, am a parrot!".| |`%target%`|The `%target%` placeholder is used to make Nadeko Mention another person or phrase, it is only supported as part of the response|`.acr "Say this: " %target%`|Input: "Say this: I, @BotName, am a parrot!". Output: "I, @BotName, am a parrot!".|
Thanks to Nekai for being creative. <3 Thanks to Nekai for being creative. <3

View File

@ -1,3 +1,34 @@
# Docker Guide with DigitalOcean # NadekoBot a Discord bot
Nadeko is written in C# and Discord.net for more information visit https://github.com/Kwoth/NadekoBot
## There is no docker image for 1.0 nadeko right now. Soon. ## Install Docker
Follow the respective guide for your operating system found here https://docs.docker.com/engine/installation/
## Nadeko Setup Guide
For this guide we will be using the folder /nadeko as our config root folder.
```
docker create --name=nadeko -v /nadeko/data:/opt/NadekoBot/src/NadekoBot/bin/Release/netcoreapp1.0/data -v /nadeko/credentials.json:/opt/NadekoBot/src/NadekoBot/credentials.json kwoth/nadeko:dev
```
-If you are coming from a previous version of nadeko (the old docker) make sure your crednetials.json has been copied into this directory and is the only thing in this folder.
-If you are making a fresh install, create your credentials.json from the following guide and palce it in the /nadeko folder
http://nadekobot.readthedocs.io/en/latest/JSON%20Explanations/
Next start the docker up with
```docker start nadeko; docker logs -f nadeko```
The docker will start and the log file will start scrolling past. Depending on hardware the bot start can take up to 5 minutes on a small DigitalOcean droplet.
Once the log ends with "NadekoBot | Starting NadekoBot v1.0-rc2" the bot is ready and can be invited to your server. Ctrl+C at this point to stop viewing the logs.
After a few moments you should be able to invite Nadeko to your server. If you cannot check the log file for errors
## Updates / Monitoring
* Upgrade to the latest version of Nadeko simply `docker restart nadeko`.
* Monitor the logs of the container in realtime `docker logs -f nadeko`.
If you have any issues with the docker setup, please ask in #help but indicate you are using the docker.
For information about configuring your bot or its functionality, please check the http://nadekobot.readthedocs.io/en/latest guides.

View File

@ -95,7 +95,7 @@ sudo apt-get update && sudo apt-get install ffmpeg -y
Use the following command to get and run `linuxAIO.sh`: Use the following command to get and run `linuxAIO.sh`:
(Remember **DO NOT** rename the file `linuxAIO.sh`) (Remember **DO NOT** rename the file `linuxAIO.sh`)
`cd ~ && wget https://github.com/Kwoth/NadekoBot-BashScript/raw/master/linuxAIO.sh && bash linuxAIO.sh` `cd ~ && wget -N https://github.com/Kwoth/NadekoBot-BashScript/raw/master/linuxAIO.sh && bash linuxAIO.sh`
Follow the on screen instructions: Follow the on screen instructions:

View File

@ -58,7 +58,7 @@ A dialog box will open asking if you want to install `xcode-select`. Select inst
Use the following command to get and run `linuxAIO.sh`: Use the following command to get and run `linuxAIO.sh`:
(Remember **DO NOT** rename the file `linuxAIO.sh`) (Remember **DO NOT** rename the file `linuxAIO.sh`)
`cd ~ && wget https://github.com/Kwoth/NadekoBot-BashScript/raw/master/linuxAIO.sh && bash linuxAIO.sh` `cd ~ && wget -N https://github.com/Kwoth/NadekoBot-BashScript/raw/master/linuxAIO.sh && bash linuxAIO.sh`
Follow the on screen instructions: Follow the on screen instructions:

View File

@ -1,6 +1,69 @@
@ECHO off @ECHO off
@TITLE NadekoBot @TITLE NadekoBot
:auto
SET root=%~dp0
CD /D %root%
CLS
ECHO Welcome to NadekoBot Auto Restart and Update!
ECHO --------------------------------------------
ECHO 1.Auto Restart and Update with Dev Build (latest)
ECHO 2.Auto Restart and Update with Stable Build
ECHO 3.Run Auto Restart normally without Updating (will restart faster)
ECHO 4.To exit
ECHO.
CHOICE /C 1234 /M "Enter your choice:"
:: Note - list ERRORLEVELS in decreasing order
IF ERRORLEVEL 4 GOTO exit
IF ERRORLEVEL 3 GOTO autorun
IF ERRORLEVEL 2 GOTO stablear
IF ERRORLEVEL 1 GOTO latestar
:latestar
ECHO Auto Restart and Update with Dev Build (latest)
ECHO Bot will auto update on every restart!
timeout /t 3
CD /D %~dp0NadekoBot\src\NadekoBot CD /D %~dp0NadekoBot\src\NadekoBot
dotnet run --configuration Release dotnet run --configuration Release
goto auto ECHO Updating...
timeout /t 3
SET "FILENAME=%~dp0\Latest.bat"
bitsadmin.exe /transfer "Downloading Nadeko (Latest)" /priority high https://github.com/Kwoth/NadekoBot/raw/master/scripts/Latest.bat "%FILENAME%"
ECHO NadekoBot Dev Build (latest) downloaded.
SET root=%~dp0
CD /D %root%
CALL Latest.bat
GOTO latestar
:stablear
ECHO Auto Restart and Update with Stable Build
ECHO Bot will auto update on every restart!
timeout /t 3
CD /D %~dp0NadekoBot\src\NadekoBot
dotnet run --configuration Release
ECHO Updating...
timeout /t 3
SET "FILENAME=%~dp0\Stable.bat"
bitsadmin.exe /transfer "Downloading Nadeko (Stable)" /priority high https://github.com/Kwoth/NadekoBot/raw/master/scripts/Stable.bat "%FILENAME%"
ECHO NadekoBot Stable build downloaded.
SET root=%~dp0
CD /D %root%
CALL Stable.bat
GOTO stablear
:autorun
ECHO Normal Auto Restart
ECHO Bot will not auto update on every restart!
timeout /t 3
CD /D %~dp0NadekoBot\src\NadekoBot
dotnet run --configuration Release
goto autorun
:Exit
SET root=%~dp0
CD /D %root%
del NadekoAutoRun.bat
CALL NadekoInstaller.bat

View File

@ -0,0 +1,802 @@
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using NadekoBot.Services.Database;
using NadekoBot.Services.Database.Models;
using NadekoBot.Modules.Music.Classes;
namespace NadekoBot.Migrations
{
[DbContext(typeof(NadekoContext))]
[Migration("20161127233843_PokeGame")]
partial class PokeGame
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
modelBuilder
.HasAnnotation("ProductVersion", "1.1.0-rtm-22752");
modelBuilder.Entity("NadekoBot.Services.Database.Models.BlacklistItem", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("BotConfigId");
b.Property<ulong>("ItemId");
b.Property<int>("Type");
b.HasKey("Id");
b.HasIndex("BotConfigId");
b.ToTable("BlacklistItem");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.BotConfig", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong>("BufferSize");
b.Property<float>("CurrencyGenerationChance");
b.Property<int>("CurrencyGenerationCooldown");
b.Property<string>("CurrencyName");
b.Property<string>("CurrencyPluralName");
b.Property<string>("CurrencySign");
b.Property<string>("DMHelpString");
b.Property<bool>("ForwardMessages");
b.Property<bool>("ForwardToAllOwners");
b.Property<string>("HelpString");
b.Property<int>("MigrationVersion");
b.Property<string>("RemindMessageFormat");
b.Property<bool>("RotatingStatuses");
b.HasKey("Id");
b.ToTable("BotConfig");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.ClashCaller", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<bool>("BaseDestroyed");
b.Property<string>("CallUser");
b.Property<int>("ClashWarId");
b.Property<int?>("SequenceNumber");
b.Property<int>("Stars");
b.Property<DateTime>("TimeAdded");
b.HasKey("Id");
b.HasIndex("ClashWarId");
b.ToTable("ClashCallers");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.ClashWar", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong>("ChannelId");
b.Property<string>("EnemyClan");
b.Property<ulong>("GuildId");
b.Property<int>("Size");
b.Property<DateTime>("StartedAt");
b.Property<int>("WarState");
b.HasKey("Id");
b.ToTable("ClashOfClans");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandCooldown", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("CommandName");
b.Property<int?>("GuildConfigId");
b.Property<int>("Seconds");
b.HasKey("Id");
b.HasIndex("GuildConfigId");
b.ToTable("CommandCooldown");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.ConvertUnit", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("InternalTrigger");
b.Property<decimal>("Modifier");
b.Property<string>("UnitType");
b.HasKey("Id");
b.ToTable("ConversionUnits");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.Currency", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<long>("Amount");
b.Property<ulong>("UserId");
b.HasKey("Id");
b.HasIndex("UserId")
.IsUnique();
b.ToTable("Currency");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.CurrencyTransaction", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<long>("Amount");
b.Property<string>("Reason");
b.Property<ulong>("UserId");
b.HasKey("Id");
b.ToTable("CurrencyTransactions");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.CustomReaction", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong?>("GuildId");
b.Property<bool>("IsRegex");
b.Property<bool>("OwnerOnly");
b.Property<string>("Response");
b.Property<string>("Trigger");
b.HasKey("Id");
b.ToTable("CustomReactions");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.Donator", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("Amount");
b.Property<string>("Name");
b.Property<ulong>("UserId");
b.HasKey("Id");
b.HasIndex("UserId")
.IsUnique();
b.ToTable("Donators");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.EightBallResponse", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("BotConfigId");
b.Property<string>("Text");
b.HasKey("Id");
b.HasIndex("BotConfigId");
b.ToTable("EightBallResponses");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterChannelId", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong>("ChannelId");
b.Property<int?>("GuildConfigId");
b.Property<int?>("GuildConfigId1");
b.HasKey("Id");
b.HasIndex("GuildConfigId");
b.HasIndex("GuildConfigId1");
b.ToTable("FilterChannelId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.FilteredWord", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("GuildConfigId");
b.Property<string>("Word");
b.HasKey("Id");
b.HasIndex("GuildConfigId");
b.ToTable("FilteredWord");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.FollowedStream", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong>("ChannelId");
b.Property<int?>("GuildConfigId");
b.Property<ulong>("GuildId");
b.Property<int>("Type");
b.Property<string>("Username");
b.HasKey("Id");
b.HasIndex("GuildConfigId");
b.ToTable("FollowedStream");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.GCChannelId", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong>("ChannelId");
b.Property<int?>("GuildConfigId");
b.HasKey("Id");
b.HasIndex("GuildConfigId");
b.ToTable("GCChannelId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.GuildConfig", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong>("AutoAssignRoleId");
b.Property<bool>("AutoDeleteByeMessages");
b.Property<int>("AutoDeleteByeMessagesTimer");
b.Property<bool>("AutoDeleteGreetMessages");
b.Property<int>("AutoDeleteGreetMessagesTimer");
b.Property<bool>("AutoDeleteSelfAssignedRoleMessages");
b.Property<ulong>("ByeMessageChannelId");
b.Property<string>("ChannelByeMessageText");
b.Property<string>("ChannelGreetMessageText");
b.Property<bool>("CleverbotEnabled");
b.Property<float>("DefaultMusicVolume");
b.Property<bool>("DeleteMessageOnCommand");
b.Property<string>("DmGreetMessageText");
b.Property<bool>("ExclusiveSelfAssignedRoles");
b.Property<bool>("FilterInvites");
b.Property<bool>("FilterWords");
b.Property<ulong>("GreetMessageChannelId");
b.Property<ulong>("GuildId");
b.Property<int?>("LogSettingId");
b.Property<string>("MuteRoleName");
b.Property<string>("PermissionRole");
b.Property<int?>("RootPermissionId");
b.Property<bool>("SendChannelByeMessage");
b.Property<bool>("SendChannelGreetMessage");
b.Property<bool>("SendDmGreetMessage");
b.Property<bool>("VerbosePermissions");
b.Property<bool>("VoicePlusTextEnabled");
b.HasKey("Id");
b.HasIndex("GuildId")
.IsUnique();
b.HasIndex("LogSettingId");
b.HasIndex("RootPermissionId");
b.ToTable("GuildConfigs");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredLogChannel", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong>("ChannelId");
b.Property<int?>("LogSettingId");
b.HasKey("Id");
b.HasIndex("LogSettingId");
b.ToTable("IgnoredLogChannels");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredVoicePresenceChannel", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong>("ChannelId");
b.Property<int?>("LogSettingId");
b.HasKey("Id");
b.HasIndex("LogSettingId");
b.ToTable("IgnoredVoicePresenceCHannels");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.LogSetting", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<bool>("ChannelCreated");
b.Property<bool>("ChannelDestroyed");
b.Property<ulong>("ChannelId");
b.Property<bool>("ChannelUpdated");
b.Property<bool>("IsLogging");
b.Property<bool>("LogUserPresence");
b.Property<bool>("LogVoicePresence");
b.Property<bool>("MessageDeleted");
b.Property<bool>("MessageUpdated");
b.Property<bool>("UserBanned");
b.Property<bool>("UserJoined");
b.Property<bool>("UserLeft");
b.Property<ulong>("UserPresenceChannelId");
b.Property<bool>("UserUnbanned");
b.Property<bool>("UserUpdated");
b.Property<ulong>("VoicePresenceChannelId");
b.HasKey("Id");
b.ToTable("LogSettings");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.ModulePrefix", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("BotConfigId");
b.Property<string>("ModuleName");
b.Property<string>("Prefix");
b.HasKey("Id");
b.HasIndex("BotConfigId");
b.ToTable("ModulePrefixes");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.MusicPlaylist", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("Author");
b.Property<ulong>("AuthorId");
b.Property<string>("Name");
b.HasKey("Id");
b.ToTable("MusicPlaylists");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.Permission", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("NextId");
b.Property<int>("PrimaryTarget");
b.Property<ulong>("PrimaryTargetId");
b.Property<int>("SecondaryTarget");
b.Property<string>("SecondaryTargetName");
b.Property<bool>("State");
b.HasKey("Id");
b.HasIndex("NextId")
.IsUnique();
b.ToTable("Permission");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.PlayingStatus", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("BotConfigId");
b.Property<string>("Status");
b.HasKey("Id");
b.HasIndex("BotConfigId");
b.ToTable("PlayingStatus");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.PlaylistSong", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("MusicPlaylistId");
b.Property<string>("Provider");
b.Property<int>("ProviderType");
b.Property<string>("Query");
b.Property<string>("Title");
b.Property<string>("Uri");
b.HasKey("Id");
b.HasIndex("MusicPlaylistId");
b.ToTable("PlaylistSong");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.Quote", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong>("AuthorId");
b.Property<string>("AuthorName")
.IsRequired();
b.Property<ulong>("GuildId");
b.Property<string>("Keyword")
.IsRequired();
b.Property<string>("Text")
.IsRequired();
b.HasKey("Id");
b.ToTable("Quotes");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.RaceAnimal", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("BotConfigId");
b.Property<string>("Icon");
b.Property<string>("Name");
b.HasKey("Id");
b.HasIndex("BotConfigId");
b.ToTable("RaceAnimals");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.Reminder", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong>("ChannelId");
b.Property<bool>("IsPrivate");
b.Property<string>("Message");
b.Property<ulong>("ServerId");
b.Property<ulong>("UserId");
b.Property<DateTime>("When");
b.HasKey("Id");
b.ToTable("Reminders");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.Repeater", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong>("ChannelId");
b.Property<ulong>("GuildId");
b.Property<TimeSpan>("Interval");
b.Property<string>("Message");
b.HasKey("Id");
b.HasIndex("ChannelId")
.IsUnique();
b.ToTable("Repeaters");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.SelfAssignedRole", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong>("GuildId");
b.Property<ulong>("RoleId");
b.HasKey("Id");
b.HasIndex("GuildId", "RoleId")
.IsUnique();
b.ToTable("SelfAssignableRoles");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.UserPokeTypes", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<long>("UserId");
b.Property<string>("type");
b.HasKey("Id");
b.HasIndex("UserId")
.IsUnique();
b.ToTable("PokeGame");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.BlacklistItem", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.BotConfig")
.WithMany("Blacklist")
.HasForeignKey("BotConfigId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.ClashCaller", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.ClashWar", "ClashWar")
.WithMany("Bases")
.HasForeignKey("ClashWarId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandCooldown", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig")
.WithMany("CommandCooldowns")
.HasForeignKey("GuildConfigId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.EightBallResponse", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.BotConfig")
.WithMany("EightBallResponses")
.HasForeignKey("BotConfigId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterChannelId", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig")
.WithMany("FilterInvitesChannelIds")
.HasForeignKey("GuildConfigId");
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig")
.WithMany("FilterWordsChannelIds")
.HasForeignKey("GuildConfigId1");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.FilteredWord", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig")
.WithMany("FilteredWords")
.HasForeignKey("GuildConfigId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.FollowedStream", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig")
.WithMany("FollowedStreams")
.HasForeignKey("GuildConfigId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.GCChannelId", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig")
.WithMany("GenerateCurrencyChannelIds")
.HasForeignKey("GuildConfigId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.GuildConfig", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.LogSetting", "LogSetting")
.WithMany()
.HasForeignKey("LogSettingId");
b.HasOne("NadekoBot.Services.Database.Models.Permission", "RootPermission")
.WithMany()
.HasForeignKey("RootPermissionId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredLogChannel", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.LogSetting", "LogSetting")
.WithMany("IgnoredChannels")
.HasForeignKey("LogSettingId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredVoicePresenceChannel", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.LogSetting", "LogSetting")
.WithMany("IgnoredVoicePresenceChannelIds")
.HasForeignKey("LogSettingId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.ModulePrefix", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.BotConfig")
.WithMany("ModulePrefixes")
.HasForeignKey("BotConfigId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.Permission", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.Permission", "Next")
.WithOne("Previous")
.HasForeignKey("NadekoBot.Services.Database.Models.Permission", "NextId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.PlayingStatus", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.BotConfig")
.WithMany("RotatingStatusMessages")
.HasForeignKey("BotConfigId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.PlaylistSong", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.MusicPlaylist")
.WithMany("Songs")
.HasForeignKey("MusicPlaylistId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.RaceAnimal", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.BotConfig")
.WithMany("RaceAnimals")
.HasForeignKey("BotConfigId");
});
}
}
}

View File

@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Migrations;
namespace NadekoBot.Migrations
{
public partial class PokeGame : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "PokeGame",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("Sqlite:Autoincrement", true),
UserId = table.Column<ulong>(nullable: false),
type = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_PokeGame", x => x.Id);
});
migrationBuilder.CreateIndex(
name: "IX_PokeGame_UserId",
table: "PokeGame",
column: "UserId",
unique: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "PokeGame");
}
}
}

View File

@ -0,0 +1,825 @@
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using NadekoBot.Services.Database;
using NadekoBot.Services.Database.Models;
using NadekoBot.Modules.Music.Classes;
namespace NadekoBot.Migrations
{
[DbContext(typeof(NadekoContext))]
[Migration("20161213025624_mutedusers")]
partial class mutedusers
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
modelBuilder
.HasAnnotation("ProductVersion", "1.1.0-rtm-22752");
modelBuilder.Entity("NadekoBot.Services.Database.Models.BlacklistItem", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("BotConfigId");
b.Property<ulong>("ItemId");
b.Property<int>("Type");
b.HasKey("Id");
b.HasIndex("BotConfigId");
b.ToTable("BlacklistItem");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.BotConfig", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong>("BufferSize");
b.Property<float>("CurrencyGenerationChance");
b.Property<int>("CurrencyGenerationCooldown");
b.Property<string>("CurrencyName");
b.Property<string>("CurrencyPluralName");
b.Property<string>("CurrencySign");
b.Property<string>("DMHelpString");
b.Property<bool>("ForwardMessages");
b.Property<bool>("ForwardToAllOwners");
b.Property<string>("HelpString");
b.Property<int>("MigrationVersion");
b.Property<string>("RemindMessageFormat");
b.Property<bool>("RotatingStatuses");
b.HasKey("Id");
b.ToTable("BotConfig");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.ClashCaller", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<bool>("BaseDestroyed");
b.Property<string>("CallUser");
b.Property<int>("ClashWarId");
b.Property<int?>("SequenceNumber");
b.Property<int>("Stars");
b.Property<DateTime>("TimeAdded");
b.HasKey("Id");
b.HasIndex("ClashWarId");
b.ToTable("ClashCallers");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.ClashWar", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong>("ChannelId");
b.Property<string>("EnemyClan");
b.Property<ulong>("GuildId");
b.Property<int>("Size");
b.Property<DateTime>("StartedAt");
b.Property<int>("WarState");
b.HasKey("Id");
b.ToTable("ClashOfClans");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandCooldown", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("CommandName");
b.Property<int?>("GuildConfigId");
b.Property<int>("Seconds");
b.HasKey("Id");
b.HasIndex("GuildConfigId");
b.ToTable("CommandCooldown");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.ConvertUnit", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("InternalTrigger");
b.Property<decimal>("Modifier");
b.Property<string>("UnitType");
b.HasKey("Id");
b.ToTable("ConversionUnits");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.Currency", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<long>("Amount");
b.Property<ulong>("UserId");
b.HasKey("Id");
b.HasIndex("UserId")
.IsUnique();
b.ToTable("Currency");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.CurrencyTransaction", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<long>("Amount");
b.Property<string>("Reason");
b.Property<ulong>("UserId");
b.HasKey("Id");
b.ToTable("CurrencyTransactions");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.CustomReaction", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong?>("GuildId");
b.Property<bool>("IsRegex");
b.Property<bool>("OwnerOnly");
b.Property<string>("Response");
b.Property<string>("Trigger");
b.HasKey("Id");
b.ToTable("CustomReactions");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.Donator", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("Amount");
b.Property<string>("Name");
b.Property<ulong>("UserId");
b.HasKey("Id");
b.HasIndex("UserId")
.IsUnique();
b.ToTable("Donators");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.EightBallResponse", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("BotConfigId");
b.Property<string>("Text");
b.HasKey("Id");
b.HasIndex("BotConfigId");
b.ToTable("EightBallResponses");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterChannelId", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong>("ChannelId");
b.Property<int?>("GuildConfigId");
b.Property<int?>("GuildConfigId1");
b.HasKey("Id");
b.HasIndex("GuildConfigId");
b.HasIndex("GuildConfigId1");
b.ToTable("FilterChannelId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.FilteredWord", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("GuildConfigId");
b.Property<string>("Word");
b.HasKey("Id");
b.HasIndex("GuildConfigId");
b.ToTable("FilteredWord");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.FollowedStream", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong>("ChannelId");
b.Property<int?>("GuildConfigId");
b.Property<ulong>("GuildId");
b.Property<int>("Type");
b.Property<string>("Username");
b.HasKey("Id");
b.HasIndex("GuildConfigId");
b.ToTable("FollowedStream");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.GCChannelId", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong>("ChannelId");
b.Property<int?>("GuildConfigId");
b.HasKey("Id");
b.HasIndex("GuildConfigId");
b.ToTable("GCChannelId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.GuildConfig", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong>("AutoAssignRoleId");
b.Property<bool>("AutoDeleteByeMessages");
b.Property<int>("AutoDeleteByeMessagesTimer");
b.Property<bool>("AutoDeleteGreetMessages");
b.Property<int>("AutoDeleteGreetMessagesTimer");
b.Property<bool>("AutoDeleteSelfAssignedRoleMessages");
b.Property<ulong>("ByeMessageChannelId");
b.Property<string>("ChannelByeMessageText");
b.Property<string>("ChannelGreetMessageText");
b.Property<bool>("CleverbotEnabled");
b.Property<float>("DefaultMusicVolume");
b.Property<bool>("DeleteMessageOnCommand");
b.Property<string>("DmGreetMessageText");
b.Property<bool>("ExclusiveSelfAssignedRoles");
b.Property<bool>("FilterInvites");
b.Property<bool>("FilterWords");
b.Property<ulong>("GreetMessageChannelId");
b.Property<ulong>("GuildId");
b.Property<int?>("LogSettingId");
b.Property<string>("MuteRoleName");
b.Property<string>("PermissionRole");
b.Property<int?>("RootPermissionId");
b.Property<bool>("SendChannelByeMessage");
b.Property<bool>("SendChannelGreetMessage");
b.Property<bool>("SendDmGreetMessage");
b.Property<bool>("VerbosePermissions");
b.Property<bool>("VoicePlusTextEnabled");
b.HasKey("Id");
b.HasIndex("GuildId")
.IsUnique();
b.HasIndex("LogSettingId");
b.HasIndex("RootPermissionId");
b.ToTable("GuildConfigs");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredLogChannel", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong>("ChannelId");
b.Property<int?>("LogSettingId");
b.HasKey("Id");
b.HasIndex("LogSettingId");
b.ToTable("IgnoredLogChannels");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredVoicePresenceChannel", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong>("ChannelId");
b.Property<int?>("LogSettingId");
b.HasKey("Id");
b.HasIndex("LogSettingId");
b.ToTable("IgnoredVoicePresenceCHannels");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.LogSetting", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<bool>("ChannelCreated");
b.Property<bool>("ChannelDestroyed");
b.Property<ulong>("ChannelId");
b.Property<bool>("ChannelUpdated");
b.Property<bool>("IsLogging");
b.Property<bool>("LogUserPresence");
b.Property<bool>("LogVoicePresence");
b.Property<bool>("MessageDeleted");
b.Property<bool>("MessageUpdated");
b.Property<bool>("UserBanned");
b.Property<bool>("UserJoined");
b.Property<bool>("UserLeft");
b.Property<ulong>("UserPresenceChannelId");
b.Property<bool>("UserUnbanned");
b.Property<bool>("UserUpdated");
b.Property<ulong>("VoicePresenceChannelId");
b.HasKey("Id");
b.ToTable("LogSettings");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.ModulePrefix", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("BotConfigId");
b.Property<string>("ModuleName");
b.Property<string>("Prefix");
b.HasKey("Id");
b.HasIndex("BotConfigId");
b.ToTable("ModulePrefixes");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.MusicPlaylist", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("Author");
b.Property<ulong>("AuthorId");
b.Property<string>("Name");
b.HasKey("Id");
b.ToTable("MusicPlaylists");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.MutedUserId", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("GuildConfigId");
b.Property<ulong>("UserId");
b.HasKey("Id");
b.HasIndex("GuildConfigId");
b.ToTable("MutedUserId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.Permission", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("NextId");
b.Property<int>("PrimaryTarget");
b.Property<ulong>("PrimaryTargetId");
b.Property<int>("SecondaryTarget");
b.Property<string>("SecondaryTargetName");
b.Property<bool>("State");
b.HasKey("Id");
b.HasIndex("NextId")
.IsUnique();
b.ToTable("Permission");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.PlayingStatus", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("BotConfigId");
b.Property<string>("Status");
b.HasKey("Id");
b.HasIndex("BotConfigId");
b.ToTable("PlayingStatus");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.PlaylistSong", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("MusicPlaylistId");
b.Property<string>("Provider");
b.Property<int>("ProviderType");
b.Property<string>("Query");
b.Property<string>("Title");
b.Property<string>("Uri");
b.HasKey("Id");
b.HasIndex("MusicPlaylistId");
b.ToTable("PlaylistSong");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.Quote", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong>("AuthorId");
b.Property<string>("AuthorName")
.IsRequired();
b.Property<ulong>("GuildId");
b.Property<string>("Keyword")
.IsRequired();
b.Property<string>("Text")
.IsRequired();
b.HasKey("Id");
b.ToTable("Quotes");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.RaceAnimal", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("BotConfigId");
b.Property<string>("Icon");
b.Property<string>("Name");
b.HasKey("Id");
b.HasIndex("BotConfigId");
b.ToTable("RaceAnimals");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.Reminder", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong>("ChannelId");
b.Property<bool>("IsPrivate");
b.Property<string>("Message");
b.Property<ulong>("ServerId");
b.Property<ulong>("UserId");
b.Property<DateTime>("When");
b.HasKey("Id");
b.ToTable("Reminders");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.Repeater", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong>("ChannelId");
b.Property<ulong>("GuildId");
b.Property<TimeSpan>("Interval");
b.Property<string>("Message");
b.HasKey("Id");
b.HasIndex("ChannelId")
.IsUnique();
b.ToTable("Repeaters");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.SelfAssignedRole", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong>("GuildId");
b.Property<ulong>("RoleId");
b.HasKey("Id");
b.HasIndex("GuildId", "RoleId")
.IsUnique();
b.ToTable("SelfAssignableRoles");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.UserPokeTypes", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong>("UserId");
b.Property<string>("type");
b.HasKey("Id");
b.HasIndex("UserId")
.IsUnique();
b.ToTable("PokeGame");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.BlacklistItem", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.BotConfig")
.WithMany("Blacklist")
.HasForeignKey("BotConfigId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.ClashCaller", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.ClashWar", "ClashWar")
.WithMany("Bases")
.HasForeignKey("ClashWarId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandCooldown", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig")
.WithMany("CommandCooldowns")
.HasForeignKey("GuildConfigId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.EightBallResponse", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.BotConfig")
.WithMany("EightBallResponses")
.HasForeignKey("BotConfigId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterChannelId", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig")
.WithMany("FilterInvitesChannelIds")
.HasForeignKey("GuildConfigId");
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig")
.WithMany("FilterWordsChannelIds")
.HasForeignKey("GuildConfigId1");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.FilteredWord", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig")
.WithMany("FilteredWords")
.HasForeignKey("GuildConfigId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.FollowedStream", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig")
.WithMany("FollowedStreams")
.HasForeignKey("GuildConfigId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.GCChannelId", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig")
.WithMany("GenerateCurrencyChannelIds")
.HasForeignKey("GuildConfigId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.GuildConfig", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.LogSetting", "LogSetting")
.WithMany()
.HasForeignKey("LogSettingId");
b.HasOne("NadekoBot.Services.Database.Models.Permission", "RootPermission")
.WithMany()
.HasForeignKey("RootPermissionId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredLogChannel", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.LogSetting", "LogSetting")
.WithMany("IgnoredChannels")
.HasForeignKey("LogSettingId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredVoicePresenceChannel", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.LogSetting", "LogSetting")
.WithMany("IgnoredVoicePresenceChannelIds")
.HasForeignKey("LogSettingId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.ModulePrefix", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.BotConfig")
.WithMany("ModulePrefixes")
.HasForeignKey("BotConfigId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.MutedUserId", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig")
.WithMany("MutedUsers")
.HasForeignKey("GuildConfigId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.Permission", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.Permission", "Next")
.WithOne("Previous")
.HasForeignKey("NadekoBot.Services.Database.Models.Permission", "NextId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.PlayingStatus", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.BotConfig")
.WithMany("RotatingStatusMessages")
.HasForeignKey("BotConfigId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.PlaylistSong", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.MusicPlaylist")
.WithMany("Songs")
.HasForeignKey("MusicPlaylistId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.RaceAnimal", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.BotConfig")
.WithMany("RaceAnimals")
.HasForeignKey("BotConfigId");
});
}
}
}

View File

@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Migrations;
namespace NadekoBot.Migrations
{
public partial class mutedusers : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "MutedUserId",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("Sqlite:Autoincrement", true),
GuildConfigId = table.Column<int>(nullable: true),
UserId = table.Column<ulong>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_MutedUserId", x => x.Id);
table.ForeignKey(
name: "FK_MutedUserId_GuildConfigs_GuildConfigId",
column: x => x.GuildConfigId,
principalTable: "GuildConfigs",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateIndex(
name: "IX_MutedUserId_GuildConfigId",
table: "MutedUserId",
column: "GuildConfigId");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "MutedUserId");
}
}
}

View File

@ -0,0 +1,855 @@
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using NadekoBot.Services.Database;
using NadekoBot.Services.Database.Models;
using NadekoBot.Modules.Music.Classes;
namespace NadekoBot.Migrations
{
[DbContext(typeof(NadekoContext))]
[Migration("20161224032833_logsettings")]
partial class logsettings
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
modelBuilder
.HasAnnotation("ProductVersion", "1.1.0-rtm-22752");
modelBuilder.Entity("NadekoBot.Services.Database.Models.BlacklistItem", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("BotConfigId");
b.Property<ulong>("ItemId");
b.Property<int>("Type");
b.HasKey("Id");
b.HasIndex("BotConfigId");
b.ToTable("BlacklistItem");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.BotConfig", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong>("BufferSize");
b.Property<float>("CurrencyGenerationChance");
b.Property<int>("CurrencyGenerationCooldown");
b.Property<string>("CurrencyName");
b.Property<string>("CurrencyPluralName");
b.Property<string>("CurrencySign");
b.Property<string>("DMHelpString");
b.Property<bool>("ForwardMessages");
b.Property<bool>("ForwardToAllOwners");
b.Property<string>("HelpString");
b.Property<int>("MigrationVersion");
b.Property<string>("RemindMessageFormat");
b.Property<bool>("RotatingStatuses");
b.HasKey("Id");
b.ToTable("BotConfig");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.ClashCaller", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<bool>("BaseDestroyed");
b.Property<string>("CallUser");
b.Property<int>("ClashWarId");
b.Property<int?>("SequenceNumber");
b.Property<int>("Stars");
b.Property<DateTime>("TimeAdded");
b.HasKey("Id");
b.HasIndex("ClashWarId");
b.ToTable("ClashCallers");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.ClashWar", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong>("ChannelId");
b.Property<string>("EnemyClan");
b.Property<ulong>("GuildId");
b.Property<int>("Size");
b.Property<DateTime>("StartedAt");
b.Property<int>("WarState");
b.HasKey("Id");
b.ToTable("ClashOfClans");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandCooldown", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("CommandName");
b.Property<int?>("GuildConfigId");
b.Property<int>("Seconds");
b.HasKey("Id");
b.HasIndex("GuildConfigId");
b.ToTable("CommandCooldown");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.ConvertUnit", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("InternalTrigger");
b.Property<decimal>("Modifier");
b.Property<string>("UnitType");
b.HasKey("Id");
b.ToTable("ConversionUnits");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.Currency", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<long>("Amount");
b.Property<ulong>("UserId");
b.HasKey("Id");
b.HasIndex("UserId")
.IsUnique();
b.ToTable("Currency");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.CurrencyTransaction", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<long>("Amount");
b.Property<string>("Reason");
b.Property<ulong>("UserId");
b.HasKey("Id");
b.ToTable("CurrencyTransactions");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.CustomReaction", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong?>("GuildId");
b.Property<bool>("IsRegex");
b.Property<bool>("OwnerOnly");
b.Property<string>("Response");
b.Property<string>("Trigger");
b.HasKey("Id");
b.ToTable("CustomReactions");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.Donator", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("Amount");
b.Property<string>("Name");
b.Property<ulong>("UserId");
b.HasKey("Id");
b.HasIndex("UserId")
.IsUnique();
b.ToTable("Donators");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.EightBallResponse", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("BotConfigId");
b.Property<string>("Text");
b.HasKey("Id");
b.HasIndex("BotConfigId");
b.ToTable("EightBallResponses");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterChannelId", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong>("ChannelId");
b.Property<int?>("GuildConfigId");
b.Property<int?>("GuildConfigId1");
b.HasKey("Id");
b.HasIndex("GuildConfigId");
b.HasIndex("GuildConfigId1");
b.ToTable("FilterChannelId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.FilteredWord", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("GuildConfigId");
b.Property<string>("Word");
b.HasKey("Id");
b.HasIndex("GuildConfigId");
b.ToTable("FilteredWord");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.FollowedStream", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong>("ChannelId");
b.Property<int?>("GuildConfigId");
b.Property<ulong>("GuildId");
b.Property<int>("Type");
b.Property<string>("Username");
b.HasKey("Id");
b.HasIndex("GuildConfigId");
b.ToTable("FollowedStream");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.GCChannelId", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong>("ChannelId");
b.Property<int?>("GuildConfigId");
b.HasKey("Id");
b.HasIndex("GuildConfigId");
b.ToTable("GCChannelId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.GuildConfig", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong>("AutoAssignRoleId");
b.Property<bool>("AutoDeleteByeMessages");
b.Property<int>("AutoDeleteByeMessagesTimer");
b.Property<bool>("AutoDeleteGreetMessages");
b.Property<int>("AutoDeleteGreetMessagesTimer");
b.Property<bool>("AutoDeleteSelfAssignedRoleMessages");
b.Property<ulong>("ByeMessageChannelId");
b.Property<string>("ChannelByeMessageText");
b.Property<string>("ChannelGreetMessageText");
b.Property<bool>("CleverbotEnabled");
b.Property<float>("DefaultMusicVolume");
b.Property<bool>("DeleteMessageOnCommand");
b.Property<string>("DmGreetMessageText");
b.Property<bool>("ExclusiveSelfAssignedRoles");
b.Property<bool>("FilterInvites");
b.Property<bool>("FilterWords");
b.Property<ulong>("GreetMessageChannelId");
b.Property<ulong>("GuildId");
b.Property<int?>("LogSettingId");
b.Property<string>("MuteRoleName");
b.Property<string>("PermissionRole");
b.Property<int?>("RootPermissionId");
b.Property<bool>("SendChannelByeMessage");
b.Property<bool>("SendChannelGreetMessage");
b.Property<bool>("SendDmGreetMessage");
b.Property<bool>("VerbosePermissions");
b.Property<bool>("VoicePlusTextEnabled");
b.HasKey("Id");
b.HasIndex("GuildId")
.IsUnique();
b.HasIndex("LogSettingId");
b.HasIndex("RootPermissionId");
b.ToTable("GuildConfigs");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredLogChannel", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong>("ChannelId");
b.Property<int?>("LogSettingId");
b.HasKey("Id");
b.HasIndex("LogSettingId");
b.ToTable("IgnoredLogChannels");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredVoicePresenceChannel", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong>("ChannelId");
b.Property<int?>("LogSettingId");
b.HasKey("Id");
b.HasIndex("LogSettingId");
b.ToTable("IgnoredVoicePresenceCHannels");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.LogSetting", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<bool>("ChannelCreated");
b.Property<ulong?>("ChannelCreatedId");
b.Property<bool>("ChannelDestroyed");
b.Property<ulong?>("ChannelDestroyedId");
b.Property<ulong>("ChannelId");
b.Property<bool>("ChannelUpdated");
b.Property<ulong?>("ChannelUpdatedId");
b.Property<bool>("IsLogging");
b.Property<ulong?>("LogOtherId");
b.Property<bool>("LogUserPresence");
b.Property<ulong?>("LogUserPresenceId");
b.Property<bool>("LogVoicePresence");
b.Property<ulong?>("LogVoicePresenceId");
b.Property<ulong?>("LogVoicePresenceTTSId");
b.Property<bool>("MessageDeleted");
b.Property<ulong?>("MessageDeletedId");
b.Property<bool>("MessageUpdated");
b.Property<ulong?>("MessageUpdatedId");
b.Property<bool>("UserBanned");
b.Property<ulong?>("UserBannedId");
b.Property<bool>("UserJoined");
b.Property<ulong?>("UserJoinedId");
b.Property<bool>("UserLeft");
b.Property<ulong?>("UserLeftId");
b.Property<ulong?>("UserMutedId");
b.Property<ulong>("UserPresenceChannelId");
b.Property<bool>("UserUnbanned");
b.Property<ulong?>("UserUnbannedId");
b.Property<bool>("UserUpdated");
b.Property<ulong?>("UserUpdatedId");
b.Property<ulong>("VoicePresenceChannelId");
b.HasKey("Id");
b.ToTable("LogSettings");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.ModulePrefix", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("BotConfigId");
b.Property<string>("ModuleName");
b.Property<string>("Prefix");
b.HasKey("Id");
b.HasIndex("BotConfigId");
b.ToTable("ModulePrefixes");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.MusicPlaylist", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("Author");
b.Property<ulong>("AuthorId");
b.Property<string>("Name");
b.HasKey("Id");
b.ToTable("MusicPlaylists");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.MutedUserId", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("GuildConfigId");
b.Property<ulong>("UserId");
b.HasKey("Id");
b.HasIndex("GuildConfigId");
b.ToTable("MutedUserId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.Permission", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("NextId");
b.Property<int>("PrimaryTarget");
b.Property<ulong>("PrimaryTargetId");
b.Property<int>("SecondaryTarget");
b.Property<string>("SecondaryTargetName");
b.Property<bool>("State");
b.HasKey("Id");
b.HasIndex("NextId")
.IsUnique();
b.ToTable("Permission");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.PlayingStatus", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("BotConfigId");
b.Property<string>("Status");
b.HasKey("Id");
b.HasIndex("BotConfigId");
b.ToTable("PlayingStatus");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.PlaylistSong", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("MusicPlaylistId");
b.Property<string>("Provider");
b.Property<int>("ProviderType");
b.Property<string>("Query");
b.Property<string>("Title");
b.Property<string>("Uri");
b.HasKey("Id");
b.HasIndex("MusicPlaylistId");
b.ToTable("PlaylistSong");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.Quote", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong>("AuthorId");
b.Property<string>("AuthorName")
.IsRequired();
b.Property<ulong>("GuildId");
b.Property<string>("Keyword")
.IsRequired();
b.Property<string>("Text")
.IsRequired();
b.HasKey("Id");
b.ToTable("Quotes");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.RaceAnimal", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("BotConfigId");
b.Property<string>("Icon");
b.Property<string>("Name");
b.HasKey("Id");
b.HasIndex("BotConfigId");
b.ToTable("RaceAnimals");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.Reminder", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong>("ChannelId");
b.Property<bool>("IsPrivate");
b.Property<string>("Message");
b.Property<ulong>("ServerId");
b.Property<ulong>("UserId");
b.Property<DateTime>("When");
b.HasKey("Id");
b.ToTable("Reminders");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.Repeater", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong>("ChannelId");
b.Property<ulong>("GuildId");
b.Property<TimeSpan>("Interval");
b.Property<string>("Message");
b.HasKey("Id");
b.HasIndex("ChannelId")
.IsUnique();
b.ToTable("Repeaters");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.SelfAssignedRole", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong>("GuildId");
b.Property<ulong>("RoleId");
b.HasKey("Id");
b.HasIndex("GuildId", "RoleId")
.IsUnique();
b.ToTable("SelfAssignableRoles");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.UserPokeTypes", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong>("UserId");
b.Property<string>("type");
b.HasKey("Id");
b.HasIndex("UserId")
.IsUnique();
b.ToTable("PokeGame");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.BlacklistItem", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.BotConfig")
.WithMany("Blacklist")
.HasForeignKey("BotConfigId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.ClashCaller", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.ClashWar", "ClashWar")
.WithMany("Bases")
.HasForeignKey("ClashWarId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandCooldown", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig")
.WithMany("CommandCooldowns")
.HasForeignKey("GuildConfigId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.EightBallResponse", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.BotConfig")
.WithMany("EightBallResponses")
.HasForeignKey("BotConfigId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterChannelId", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig")
.WithMany("FilterInvitesChannelIds")
.HasForeignKey("GuildConfigId");
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig")
.WithMany("FilterWordsChannelIds")
.HasForeignKey("GuildConfigId1");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.FilteredWord", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig")
.WithMany("FilteredWords")
.HasForeignKey("GuildConfigId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.FollowedStream", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig")
.WithMany("FollowedStreams")
.HasForeignKey("GuildConfigId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.GCChannelId", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig")
.WithMany("GenerateCurrencyChannelIds")
.HasForeignKey("GuildConfigId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.GuildConfig", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.LogSetting", "LogSetting")
.WithMany()
.HasForeignKey("LogSettingId");
b.HasOne("NadekoBot.Services.Database.Models.Permission", "RootPermission")
.WithMany()
.HasForeignKey("RootPermissionId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredLogChannel", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.LogSetting", "LogSetting")
.WithMany("IgnoredChannels")
.HasForeignKey("LogSettingId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredVoicePresenceChannel", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.LogSetting", "LogSetting")
.WithMany("IgnoredVoicePresenceChannelIds")
.HasForeignKey("LogSettingId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.ModulePrefix", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.BotConfig")
.WithMany("ModulePrefixes")
.HasForeignKey("BotConfigId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.MutedUserId", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig")
.WithMany("MutedUsers")
.HasForeignKey("GuildConfigId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.Permission", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.Permission", "Next")
.WithOne("Previous")
.HasForeignKey("NadekoBot.Services.Database.Models.Permission", "NextId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.PlayingStatus", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.BotConfig")
.WithMany("RotatingStatusMessages")
.HasForeignKey("BotConfigId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.PlaylistSong", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.MusicPlaylist")
.WithMany("Songs")
.HasForeignKey("MusicPlaylistId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.RaceAnimal", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.BotConfig")
.WithMany("RaceAnimals")
.HasForeignKey("BotConfigId");
});
}
}
}

View File

@ -0,0 +1,150 @@
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Migrations;
namespace NadekoBot.Migrations
{
public partial class logsettings : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<ulong>(
name: "ChannelCreatedId",
table: "LogSettings",
nullable: true);
migrationBuilder.AddColumn<ulong>(
name: "ChannelDestroyedId",
table: "LogSettings",
nullable: true);
migrationBuilder.AddColumn<ulong>(
name: "ChannelUpdatedId",
table: "LogSettings",
nullable: true);
migrationBuilder.AddColumn<ulong>(
name: "LogOtherId",
table: "LogSettings",
nullable: true);
migrationBuilder.AddColumn<ulong>(
name: "LogUserPresenceId",
table: "LogSettings",
nullable: true);
migrationBuilder.AddColumn<ulong>(
name: "LogVoicePresenceId",
table: "LogSettings",
nullable: true);
migrationBuilder.AddColumn<ulong>(
name: "LogVoicePresenceTTSId",
table: "LogSettings",
nullable: true);
migrationBuilder.AddColumn<ulong>(
name: "MessageDeletedId",
table: "LogSettings",
nullable: true);
migrationBuilder.AddColumn<ulong>(
name: "MessageUpdatedId",
table: "LogSettings",
nullable: true);
migrationBuilder.AddColumn<ulong>(
name: "UserBannedId",
table: "LogSettings",
nullable: true);
migrationBuilder.AddColumn<ulong>(
name: "UserJoinedId",
table: "LogSettings",
nullable: true);
migrationBuilder.AddColumn<ulong>(
name: "UserLeftId",
table: "LogSettings",
nullable: true);
migrationBuilder.AddColumn<ulong>(
name: "UserMutedId",
table: "LogSettings",
nullable: true);
migrationBuilder.AddColumn<ulong>(
name: "UserUnbannedId",
table: "LogSettings",
nullable: true);
migrationBuilder.AddColumn<ulong>(
name: "UserUpdatedId",
table: "LogSettings",
nullable: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "ChannelCreatedId",
table: "LogSettings");
migrationBuilder.DropColumn(
name: "ChannelDestroyedId",
table: "LogSettings");
migrationBuilder.DropColumn(
name: "ChannelUpdatedId",
table: "LogSettings");
migrationBuilder.DropColumn(
name: "LogOtherId",
table: "LogSettings");
migrationBuilder.DropColumn(
name: "LogUserPresenceId",
table: "LogSettings");
migrationBuilder.DropColumn(
name: "LogVoicePresenceId",
table: "LogSettings");
migrationBuilder.DropColumn(
name: "LogVoicePresenceTTSId",
table: "LogSettings");
migrationBuilder.DropColumn(
name: "MessageDeletedId",
table: "LogSettings");
migrationBuilder.DropColumn(
name: "MessageUpdatedId",
table: "LogSettings");
migrationBuilder.DropColumn(
name: "UserBannedId",
table: "LogSettings");
migrationBuilder.DropColumn(
name: "UserJoinedId",
table: "LogSettings");
migrationBuilder.DropColumn(
name: "UserLeftId",
table: "LogSettings");
migrationBuilder.DropColumn(
name: "UserMutedId",
table: "LogSettings");
migrationBuilder.DropColumn(
name: "UserUnbannedId",
table: "LogSettings");
migrationBuilder.DropColumn(
name: "UserUpdatedId",
table: "LogSettings");
}
}
}

View File

@ -423,34 +423,64 @@ namespace NadekoBot.Migrations
b.Property<bool>("ChannelCreated"); b.Property<bool>("ChannelCreated");
b.Property<ulong?>("ChannelCreatedId");
b.Property<bool>("ChannelDestroyed"); b.Property<bool>("ChannelDestroyed");
b.Property<ulong?>("ChannelDestroyedId");
b.Property<ulong>("ChannelId"); b.Property<ulong>("ChannelId");
b.Property<bool>("ChannelUpdated"); b.Property<bool>("ChannelUpdated");
b.Property<ulong?>("ChannelUpdatedId");
b.Property<bool>("IsLogging"); b.Property<bool>("IsLogging");
b.Property<ulong?>("LogOtherId");
b.Property<bool>("LogUserPresence"); b.Property<bool>("LogUserPresence");
b.Property<ulong?>("LogUserPresenceId");
b.Property<bool>("LogVoicePresence"); b.Property<bool>("LogVoicePresence");
b.Property<ulong?>("LogVoicePresenceId");
b.Property<ulong?>("LogVoicePresenceTTSId");
b.Property<bool>("MessageDeleted"); b.Property<bool>("MessageDeleted");
b.Property<ulong?>("MessageDeletedId");
b.Property<bool>("MessageUpdated"); b.Property<bool>("MessageUpdated");
b.Property<ulong?>("MessageUpdatedId");
b.Property<bool>("UserBanned"); b.Property<bool>("UserBanned");
b.Property<ulong?>("UserBannedId");
b.Property<bool>("UserJoined"); b.Property<bool>("UserJoined");
b.Property<ulong?>("UserJoinedId");
b.Property<bool>("UserLeft"); b.Property<bool>("UserLeft");
b.Property<ulong?>("UserLeftId");
b.Property<ulong?>("UserMutedId");
b.Property<ulong>("UserPresenceChannelId"); b.Property<ulong>("UserPresenceChannelId");
b.Property<bool>("UserUnbanned"); b.Property<bool>("UserUnbanned");
b.Property<ulong?>("UserUnbannedId");
b.Property<bool>("UserUpdated"); b.Property<bool>("UserUpdated");
b.Property<ulong?>("UserUpdatedId");
b.Property<ulong>("VoicePresenceChannelId"); b.Property<ulong>("VoicePresenceChannelId");
b.HasKey("Id"); b.HasKey("Id");
@ -492,6 +522,22 @@ namespace NadekoBot.Migrations
b.ToTable("MusicPlaylists"); b.ToTable("MusicPlaylists");
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.MutedUserId", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("GuildConfigId");
b.Property<ulong>("UserId");
b.HasKey("Id");
b.HasIndex("GuildConfigId");
b.ToTable("MutedUserId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.Permission", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.Permission", b =>
{ {
b.Property<int>("Id") b.Property<int>("Id")
@ -658,6 +704,23 @@ namespace NadekoBot.Migrations
b.ToTable("SelfAssignableRoles"); b.ToTable("SelfAssignableRoles");
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.UserPokeTypes", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong>("UserId");
b.Property<string>("type");
b.HasKey("Id");
b.HasIndex("UserId")
.IsUnique();
b.ToTable("PokeGame");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.BlacklistItem", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.BlacklistItem", b =>
{ {
b.HasOne("NadekoBot.Services.Database.Models.BotConfig") b.HasOne("NadekoBot.Services.Database.Models.BotConfig")
@ -751,6 +814,13 @@ namespace NadekoBot.Migrations
.HasForeignKey("BotConfigId"); .HasForeignKey("BotConfigId");
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.MutedUserId", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig")
.WithMany("MutedUsers")
.HasForeignKey("GuildConfigId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.Permission", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.Permission", b =>
{ {
b.HasOne("NadekoBot.Services.Database.Models.Permission", "Next") b.HasOne("NadekoBot.Services.Database.Models.Permission", "Next")

View File

@ -15,6 +15,9 @@ using System.Net.Http;
using System.IO; using System.IO;
using static NadekoBot.Modules.Permissions.Permissions; using static NadekoBot.Modules.Permissions.Permissions;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using NLog;
using NadekoBot.Services.Database;
using Microsoft.EntityFrameworkCore;
namespace NadekoBot.Modules.Administration namespace NadekoBot.Modules.Administration
{ {
@ -24,27 +27,25 @@ namespace NadekoBot.Modules.Administration
private static ConcurrentDictionary<ulong, string> GuildMuteRoles { get; } = new ConcurrentDictionary<ulong, string>(); private static ConcurrentDictionary<ulong, string> GuildMuteRoles { get; } = new ConcurrentDictionary<ulong, string>();
public Administration(ILocalization loc, CommandService cmds, ShardedDiscordClient client) : base(loc, cmds, client) private new static Logger _log { get; }
public Administration() : base()
{ {
NadekoBot.CommandHandler.CommandExecuted += DelMsgOnCmd_Handler;
} }
static Administration() static Administration()
{ {
using (var uow = DbHandler.UnitOfWork()) _log = LogManager.GetCurrentClassLogger();
{ NadekoBot.CommandHandler.CommandExecuted += DelMsgOnCmd_Handler;
var configs = NadekoBot.AllGuildConfigs;
GuildMuteRoles = new ConcurrentDictionary<ulong, string>(configs
.Where(c=>!string.IsNullOrWhiteSpace(c.MuteRoleName))
.ToDictionary(c => c.GuildId, c => c.MuteRoleName));
}
} }
private async void DelMsgOnCmd_Handler(object sender, CommandExecutedEventArgs e) private static async Task DelMsgOnCmd_Handler(IUserMessage msg, Command cmd)
{ {
try try
{ {
var channel = e.Message.Channel as ITextChannel; var channel = msg.Channel as ITextChannel;
if (channel == null) if (channel == null)
return; return;
@ -52,11 +53,11 @@ namespace NadekoBot.Modules.Administration
bool shouldDelete; bool shouldDelete;
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
shouldDelete = uow.GuildConfigs.For(channel.Guild.Id).DeleteMessageOnCommand; shouldDelete = uow.GuildConfigs.For(channel.Guild.Id, set => set).DeleteMessageOnCommand;
} }
if (shouldDelete) if (shouldDelete)
await e.Message.DeleteAsync().ConfigureAwait(false); await msg.DeleteAsync().ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -64,39 +65,6 @@ namespace NadekoBot.Modules.Administration
} }
} }
private static async Task<IRole> GetMuteRole(IGuild guild)
{
const string defaultMuteRoleName = "nadeko-mute";
var muteRoleName = GuildMuteRoles.GetOrAdd(guild.Id, defaultMuteRoleName);
var muteRole = guild.Roles.FirstOrDefault(r => r.Name == muteRoleName);
if (muteRole == null)
{
//if it doesn't exist, create it
try { muteRole = await guild.CreateRoleAsync(muteRoleName, GuildPermissions.None).ConfigureAwait(false); }
catch
{
//if creations fails, maybe the name is not correct, find default one, if doesn't work, create default one
muteRole = guild.Roles.FirstOrDefault(r => r.Name == muteRoleName) ??
await guild.CreateRoleAsync(defaultMuteRoleName, GuildPermissions.None).ConfigureAwait(false);
}
foreach (var toOverwrite in guild.GetTextChannels())
{
try
{
await toOverwrite.AddPermissionOverwriteAsync(muteRole, new OverwritePermissions(sendMessages: PermValue.Deny, attachFiles: PermValue.Deny))
.ConfigureAwait(false);
}
catch { }
await Task.Delay(200).ConfigureAwait(false);
}
}
return muteRole;
}
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[RequirePermission(GuildPermission.Administrator)] [RequirePermission(GuildPermission.Administrator)]
@ -119,7 +87,7 @@ namespace NadekoBot.Modules.Administration
await uow.CompleteAsync(); await uow.CompleteAsync();
} }
await channel.SendMessageAsync($"{imsg.Author.Mention} 🆗 **Permissions for this server are reset.**"); await channel.SendConfirmAsync($"{imsg.Author.Mention} 🆗 **Permissions for this server are reset.**");
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -128,18 +96,18 @@ namespace NadekoBot.Modules.Administration
public async Task Delmsgoncmd(IUserMessage umsg) public async Task Delmsgoncmd(IUserMessage umsg)
{ {
var channel = (ITextChannel)umsg.Channel; var channel = (ITextChannel)umsg.Channel;
GuildConfig conf; bool enabled;
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
conf = uow.GuildConfigs.For(channel.Guild.Id); var conf = uow.GuildConfigs.For(channel.Guild.Id, set => set);
conf.DeleteMessageOnCommand = !conf.DeleteMessageOnCommand; enabled = conf.DeleteMessageOnCommand = !conf.DeleteMessageOnCommand;
uow.GuildConfigs.Update(conf);
await uow.CompleteAsync(); await uow.CompleteAsync();
} }
if (conf.DeleteMessageOnCommand) if (enabled)
await channel.SendMessageAsync("✅ **Now automatically deleting successful command invokations.**").ConfigureAwait(false); await channel.SendConfirmAsync("✅ **Now automatically deleting successful command invokations.**").ConfigureAwait(false);
else else
await channel.SendMessageAsync("❗**Stopped automatic deletion of successful command invokations.**").ConfigureAwait(false); await channel.SendConfirmAsync("❗**Stopped automatic deletion of successful command invokations.**").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -151,11 +119,11 @@ namespace NadekoBot.Modules.Administration
try try
{ {
await usr.AddRolesAsync(role).ConfigureAwait(false); await usr.AddRolesAsync(role).ConfigureAwait(false);
await channel.SendMessageAsync($" Successfully added role **{role.Name}** to user **{usr.Username}**").ConfigureAwait(false); await channel.SendConfirmAsync($" Successfully added role **{role.Name}** to user **{usr.Username}**").ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {
await channel.SendMessageAsync("⚠️ Failed to add role. **Bot has insufficient permissions.**\n").ConfigureAwait(false); await channel.SendErrorAsync("⚠️ Failed to add role. **Bot has insufficient permissions.**\n").ConfigureAwait(false);
Console.WriteLine(ex.ToString()); Console.WriteLine(ex.ToString());
} }
} }
@ -169,11 +137,11 @@ namespace NadekoBot.Modules.Administration
try try
{ {
await usr.RemoveRolesAsync(role).ConfigureAwait(false); await usr.RemoveRolesAsync(role).ConfigureAwait(false);
await channel.SendMessageAsync($" Successfully removed role **{role.Name}** from user **{usr.Username}**").ConfigureAwait(false); await channel.SendConfirmAsync($" Successfully removed role **{role.Name}** from user **{usr.Username}**").ConfigureAwait(false);
} }
catch catch
{ {
await channel.SendMessageAsync("⚠️ Failed to remove role. Most likely reason: **Insufficient permissions.**").ConfigureAwait(false); await channel.SendErrorAsync("⚠️ Failed to remove role. Most likely reason: **Insufficient permissions.**").ConfigureAwait(false);
} }
} }
@ -187,15 +155,15 @@ namespace NadekoBot.Modules.Administration
{ {
if (roleToEdit.Position > (await channel.Guild.GetCurrentUserAsync().ConfigureAwait(false)).Roles.Max(r => r.Position)) if (roleToEdit.Position > (await channel.Guild.GetCurrentUserAsync().ConfigureAwait(false)).Roles.Max(r => r.Position))
{ {
await channel.SendMessageAsync("🚫 You can't edit roles higher than your highest role.").ConfigureAwait(false); await channel.SendErrorAsync("🚫 You can't edit roles higher than your highest role.").ConfigureAwait(false);
return; return;
} }
await roleToEdit.ModifyAsync(g => g.Name = newname).ConfigureAwait(false); await roleToEdit.ModifyAsync(g => g.Name = newname).ConfigureAwait(false);
await channel.SendMessageAsync("✅ Role renamed.").ConfigureAwait(false); await channel.SendConfirmAsync("✅ Role renamed.").ConfigureAwait(false);
} }
catch (Exception) catch (Exception)
{ {
await channel.SendMessageAsync("⚠️ Failed to rename role. Probably **insufficient permissions.**").ConfigureAwait(false); await channel.SendErrorAsync("⚠️ Failed to rename role. Probably **insufficient permissions.**").ConfigureAwait(false);
} }
} }
@ -209,11 +177,11 @@ namespace NadekoBot.Modules.Administration
try try
{ {
await user.RemoveRolesAsync(user.Roles).ConfigureAwait(false); await user.RemoveRolesAsync(user.Roles).ConfigureAwait(false);
await channel.SendMessageAsync($"🗑 Successfully removed **all** roles from user **{user.Username}**").ConfigureAwait(false); await channel.SendConfirmAsync($"🗑 Successfully removed **all** roles from user **{user.Username}**").ConfigureAwait(false);
} }
catch catch
{ {
await channel.SendMessageAsync("⚠️ Failed to remove roles. Most likely reason: **Insufficient permissions.**").ConfigureAwait(false); await channel.SendErrorAsync("⚠️ Failed to remove roles. Most likely reason: **Insufficient permissions.**").ConfigureAwait(false);
} }
} }
@ -230,11 +198,11 @@ namespace NadekoBot.Modules.Administration
try try
{ {
var r = await channel.Guild.CreateRoleAsync(roleName).ConfigureAwait(false); var r = await channel.Guild.CreateRoleAsync(roleName).ConfigureAwait(false);
await channel.SendMessageAsync($"✅ Successfully created role **{r.Name}**.").ConfigureAwait(false); await channel.SendConfirmAsync($"✅ Successfully created role **{r.Name}**.").ConfigureAwait(false);
} }
catch (Exception) catch (Exception)
{ {
await channel.SendMessageAsync("⚠️ Unspecified error.").ConfigureAwait(false); await channel.SendErrorAsync("⚠️ Unspecified error.").ConfigureAwait(false);
} }
} }
@ -247,7 +215,7 @@ namespace NadekoBot.Modules.Administration
if (args.Count() != 2 && args.Count() != 4) if (args.Count() != 2 && args.Count() != 4)
{ {
await channel.SendMessageAsync("❌ The parameters specified are **invalid.**").ConfigureAwait(false); await channel.SendErrorAsync("❌ The parameters specified are **invalid.**").ConfigureAwait(false);
return; return;
} }
var roleName = args[0].ToUpperInvariant(); var roleName = args[0].ToUpperInvariant();
@ -255,7 +223,7 @@ namespace NadekoBot.Modules.Administration
if (role == null) if (role == null)
{ {
await channel.SendMessageAsync("🚫 That role **does not exist.**").ConfigureAwait(false); await channel.SendErrorAsync("🚫 That role **does not exist.**").ConfigureAwait(false);
return; return;
} }
try try
@ -268,11 +236,11 @@ namespace NadekoBot.Modules.Administration
var blue = Convert.ToByte(rgb ? int.Parse(args[3]) : Convert.ToInt32(arg1.Substring(4, 2), 16)); var blue = Convert.ToByte(rgb ? int.Parse(args[3]) : Convert.ToInt32(arg1.Substring(4, 2), 16));
await role.ModifyAsync(r => r.Color = new Discord.Color(red, green, blue).RawValue).ConfigureAwait(false); await role.ModifyAsync(r => r.Color = new Discord.Color(red, green, blue).RawValue).ConfigureAwait(false);
await channel.SendMessageAsync($"☑️ Role **{role.Name}'s** color has been changed.").ConfigureAwait(false); await channel.SendConfirmAsync($"☑️ Role **{role.Name}'s** color has been changed.").ConfigureAwait(false);
} }
catch (Exception) catch (Exception)
{ {
await channel.SendMessageAsync("⚠️ Error occured, most likely **invalid parameters** or **insufficient permissions.**").ConfigureAwait(false); await channel.SendErrorAsync("⚠️ Error occured, most likely **invalid parameters** or **insufficient permissions.**").ConfigureAwait(false);
} }
} }
@ -288,12 +256,12 @@ namespace NadekoBot.Modules.Administration
} }
if (umsg.Author.Id != user.Guild.OwnerId && user.Roles.Select(r => r.Position).Max() >= ((IGuildUser)umsg.Author).Roles.Select(r => r.Position).Max()) if (umsg.Author.Id != user.Guild.OwnerId && user.Roles.Select(r => r.Position).Max() >= ((IGuildUser)umsg.Author).Roles.Select(r => r.Position).Max())
{ {
await channel.SendMessageAsync("⚠️ You can't use this command on users with a role higher or equal to yours in the role hierarchy."); await channel.SendErrorAsync("⚠️ You can't use this command on users with a role higher or equal to yours in the role hierarchy.");
return; return;
} }
try try
{ {
await (await user.CreateDMChannelAsync()).SendMessageAsync($"⛔️ **You have been BANNED from `{channel.Guild.Name}` server.**\n" + await (await user.CreateDMChannelAsync()).SendErrorAsync($"⛔️ **You have been BANNED from `{channel.Guild.Name}` server.**\n" +
$"⚖ *Reason:* {msg}").ConfigureAwait(false); $"⚖ *Reason:* {msg}").ConfigureAwait(false);
await Task.Delay(2000).ConfigureAwait(false); await Task.Delay(2000).ConfigureAwait(false);
} }
@ -302,11 +270,11 @@ namespace NadekoBot.Modules.Administration
{ {
await channel.Guild.AddBanAsync(user, 7).ConfigureAwait(false); await channel.Guild.AddBanAsync(user, 7).ConfigureAwait(false);
await channel.SendMessageAsync("⛔️ **Banned** user **" + user.Username + "** ID: `" + user.Id + "`").ConfigureAwait(false); await channel.SendConfirmAsync("⛔️ **Banned** user **" + user.Username + "** ID: `" + user.Id + "`").ConfigureAwait(false);
} }
catch catch
{ {
await channel.SendMessageAsync("⚠️ **Error.** Most likely I don't have sufficient permissions.").ConfigureAwait(false); await channel.SendErrorAsync("⚠️ **Error.** Most likely I don't have sufficient permissions.").ConfigureAwait(false);
} }
} }
@ -323,12 +291,12 @@ namespace NadekoBot.Modules.Administration
} }
if (umsg.Author.Id != user.Guild.OwnerId && user.Roles.Select(r => r.Position).Max() >= ((IGuildUser)umsg.Author).Roles.Select(r => r.Position).Max()) if (umsg.Author.Id != user.Guild.OwnerId && user.Roles.Select(r => r.Position).Max() >= ((IGuildUser)umsg.Author).Roles.Select(r => r.Position).Max())
{ {
await channel.SendMessageAsync("⚠️ You can't use this command on users with a role higher or equal to yours in the role hierarchy."); await channel.SendErrorAsync("⚠️ You can't use this command on users with a role higher or equal to yours in the role hierarchy.");
return; return;
} }
try try
{ {
await user.SendMessageAsync($"☣ **You have been SOFT-BANNED from `{channel.Guild.Name}` server.**\n" + await user.SendErrorAsync($"☣ **You have been SOFT-BANNED from `{channel.Guild.Name}` server.**\n" +
$"⚖ *Reason:* {msg}").ConfigureAwait(false); $"⚖ *Reason:* {msg}").ConfigureAwait(false);
await Task.Delay(2000).ConfigureAwait(false); await Task.Delay(2000).ConfigureAwait(false);
} }
@ -339,11 +307,11 @@ namespace NadekoBot.Modules.Administration
try { await channel.Guild.RemoveBanAsync(user).ConfigureAwait(false); } try { await channel.Guild.RemoveBanAsync(user).ConfigureAwait(false); }
catch { await channel.Guild.RemoveBanAsync(user).ConfigureAwait(false); } catch { await channel.Guild.RemoveBanAsync(user).ConfigureAwait(false); }
await channel.SendMessageAsync("☣ **Soft-Banned** user **" + user.Username + "** ID: `" + user.Id + "`").ConfigureAwait(false); await channel.SendConfirmAsync("☣ **Soft-Banned** user **" + user.Username + "** ID: `" + user.Id + "`").ConfigureAwait(false);
} }
catch catch
{ {
await channel.SendMessageAsync("⚠️ Error. Most likely I don't have sufficient permissions.").ConfigureAwait(false); await channel.SendErrorAsync("⚠️ Error. Most likely I don't have sufficient permissions.").ConfigureAwait(false);
} }
} }
@ -356,20 +324,20 @@ namespace NadekoBot.Modules.Administration
if (user == null) if (user == null)
{ {
await channel.SendMessageAsync("❗User not found.").ConfigureAwait(false); await channel.SendErrorAsync("❗User not found.").ConfigureAwait(false);
return; return;
} }
if (umsg.Author.Id != user.Guild.OwnerId && user.Roles.Select(r => r.Position).Max() >= ((IGuildUser)umsg.Author).Roles.Select(r => r.Position).Max()) if (umsg.Author.Id != user.Guild.OwnerId && user.Roles.Select(r => r.Position).Max() >= ((IGuildUser)umsg.Author).Roles.Select(r => r.Position).Max())
{ {
await channel.SendMessageAsync("⚠️ You can't use this command on users with a role higher or equal to yours in the role hierarchy."); await channel.SendErrorAsync("⚠️ You can't use this command on users with a role higher or equal to yours in the role hierarchy.");
return; return;
} }
if (!string.IsNullOrWhiteSpace(msg)) if (!string.IsNullOrWhiteSpace(msg))
{ {
try try
{ {
await user.SendMessageAsync($"‼️**You have been KICKED from `{channel.Guild.Name}` server.**\n" + await user.SendErrorAsync($"‼️**You have been KICKED from `{channel.Guild.Name}` server.**\n" +
$"⚖ *Reason:* {msg}").ConfigureAwait(false); $"⚖ *Reason:* {msg}").ConfigureAwait(false);
await Task.Delay(2000).ConfigureAwait(false); await Task.Delay(2000).ConfigureAwait(false);
} }
@ -378,150 +346,11 @@ namespace NadekoBot.Modules.Administration
try try
{ {
await user.KickAsync().ConfigureAwait(false); await user.KickAsync().ConfigureAwait(false);
await channel.SendMessageAsync("‼️**Kicked** user **" + user.Username + "** ID: `" + user.Id + "`").ConfigureAwait(false); await channel.SendConfirmAsync("‼️**Kicked** user **" + user.Username + "** ID: `" + user.Id + "`").ConfigureAwait(false);
} }
catch catch
{ {
await channel.SendMessageAsync("⚠️ Error. Most likely I don't have sufficient permissions.").ConfigureAwait(false); await channel.SendErrorAsync("⚠️ Error. Most likely I don't have sufficient permissions.").ConfigureAwait(false);
}
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[RequirePermission(GuildPermission.ManageRoles)]
[Priority(1)]
public async Task SetMuteRole(IUserMessage imsg, [Remainder] string name)
{
var channel = (ITextChannel)imsg.Channel;
name = name.Trim();
if (string.IsNullOrWhiteSpace(name))
return;
using (var uow = DbHandler.UnitOfWork())
{
var config = uow.GuildConfigs.For(channel.Guild.Id);
config.MuteRoleName = name;
GuildMuteRoles.AddOrUpdate(channel.Guild.Id, name, (id, old) => name);
await uow.CompleteAsync().ConfigureAwait(false);
}
await channel.SendMessageAsync("☑️ **New mute role set.**").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[RequirePermission(GuildPermission.ManageRoles)]
[Priority(0)]
public Task SetMuteRole(IUserMessage imsg, [Remainder] IRole role)
=> SetMuteRole(imsg, role.Name);
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[RequirePermission(GuildPermission.ManageRoles)]
[RequirePermission(GuildPermission.MuteMembers)]
public async Task Mute(IUserMessage umsg, IGuildUser user)
{
var channel = (ITextChannel)umsg.Channel;
try
{
await user.ModifyAsync(usr => usr.Mute = true).ConfigureAwait(false);
await user.AddRolesAsync(await GetMuteRole(channel.Guild).ConfigureAwait(false)).ConfigureAwait(false);
await channel.SendMessageAsync($"🔇 **{user}** has been **muted** from text and voice chat successfully.").ConfigureAwait(false);
}
catch
{
await channel.SendMessageAsync("⚠️ I most likely don't have the permission necessary for that.").ConfigureAwait(false);
}
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[RequirePermission(GuildPermission.ManageRoles)]
[RequirePermission(GuildPermission.MuteMembers)]
public async Task Unmute(IUserMessage umsg, IGuildUser user)
{
var channel = (ITextChannel)umsg.Channel;
try
{
await user.ModifyAsync(usr => usr.Mute = false).ConfigureAwait(false);
await user.RemoveRolesAsync(await GetMuteRole(channel.Guild).ConfigureAwait(false)).ConfigureAwait(false);
await channel.SendMessageAsync($"🔉 **{user}** has been **unmuted** from text and voice chat successfully.").ConfigureAwait(false);
}
catch
{
await channel.SendMessageAsync("⚠️ I most likely don't have the permission necessary for that.").ConfigureAwait(false);
}
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[RequirePermission(GuildPermission.ManageRoles)]
public async Task ChatMute(IUserMessage umsg, IGuildUser user)
{
var channel = (ITextChannel)umsg.Channel;
try
{
await user.AddRolesAsync(await GetMuteRole(channel.Guild).ConfigureAwait(false)).ConfigureAwait(false);
await channel.SendMessageAsync($"✏️🚫 **{user}** has been **muted** from chatting successfully.").ConfigureAwait(false);
}
catch
{
await channel.SendMessageAsync("⚠️ I most likely don't have the permission necessary for that.").ConfigureAwait(false);
}
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[RequirePermission(GuildPermission.ManageRoles)]
public async Task ChatUnmute(IUserMessage umsg, IGuildUser user)
{
var channel = (ITextChannel)umsg.Channel;
try
{
await user.RemoveRolesAsync(await GetMuteRole(channel.Guild).ConfigureAwait(false)).ConfigureAwait(false);
await channel.SendMessageAsync($"✏️✅ **{user}** has been **unmuted** from chatting successfully.").ConfigureAwait(false);
}
catch
{
await channel.SendMessageAsync("⚠️ I most likely don't have the permission necessary for that.").ConfigureAwait(false);
}
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[RequirePermission(GuildPermission.MuteMembers)]
public async Task VoiceMute(IUserMessage umsg, IGuildUser user)
{
var channel = (ITextChannel)umsg.Channel;
try
{
await user.ModifyAsync(usr => usr.Mute = true).ConfigureAwait(false);
await channel.SendMessageAsync($"🎙🚫 **{user}** has been **voice muted** successfully.").ConfigureAwait(false);
}
catch
{
await channel.SendMessageAsync("⚠️ I most likely don't have the permission necessary for that.").ConfigureAwait(false);
}
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[RequirePermission(GuildPermission.MuteMembers)]
public async Task VoiceUnmute(IUserMessage umsg, IGuildUser user)
{
var channel = (ITextChannel)umsg.Channel;
try
{
await user.ModifyAsync(usr => usr.Mute = false).ConfigureAwait(false);
await channel.SendMessageAsync($"🎙✅ **{user}** has been **voice unmuted** successfully.").ConfigureAwait(false);
}
catch
{
await channel.SendMessageAsync("⚠️ I most likely don't have the permission necessary for that.").ConfigureAwait(false);
} }
} }
@ -540,11 +369,11 @@ namespace NadekoBot.Modules.Administration
{ {
await u.ModifyAsync(usr => usr.Deaf = true).ConfigureAwait(false); await u.ModifyAsync(usr => usr.Deaf = true).ConfigureAwait(false);
} }
await channel.SendMessageAsync("🔇 **Deafen** successful.").ConfigureAwait(false); await channel.SendConfirmAsync("🔇 **Deafen** successful.").ConfigureAwait(false);
} }
catch catch
{ {
await channel.SendMessageAsync("⚠️ I most likely don't have the permission necessary for that.").ConfigureAwait(false); await channel.SendErrorAsync("⚠️ I most likely don't have the permission necessary for that.").ConfigureAwait(false);
} }
} }
@ -563,11 +392,11 @@ namespace NadekoBot.Modules.Administration
{ {
await u.ModifyAsync(usr => usr.Deaf = false).ConfigureAwait(false); await u.ModifyAsync(usr => usr.Deaf = false).ConfigureAwait(false);
} }
await channel.SendMessageAsync("🔊 **Undeafen** successful.").ConfigureAwait(false); await channel.SendConfirmAsync("🔊 **Undeafen** successful.").ConfigureAwait(false);
} }
catch catch
{ {
await channel.SendMessageAsync("⚠️ I most likely don't have the permission necessary for that.").ConfigureAwait(false); await channel.SendErrorAsync("⚠️ I most likely don't have the permission necessary for that.").ConfigureAwait(false);
} }
} }
@ -577,7 +406,7 @@ namespace NadekoBot.Modules.Administration
public async Task DelVoiChanl(IUserMessage umsg, [Remainder] IVoiceChannel voiceChannel) public async Task DelVoiChanl(IUserMessage umsg, [Remainder] IVoiceChannel voiceChannel)
{ {
await voiceChannel.DeleteAsync().ConfigureAwait(false); await voiceChannel.DeleteAsync().ConfigureAwait(false);
await umsg.Channel.SendMessageAsync($"🗑 Removed voice channel **{voiceChannel.Name}** successfully.").ConfigureAwait(false); await umsg.Channel.SendConfirmAsync($"🗑 Removed voice channel **{voiceChannel.Name}** successfully.").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -587,7 +416,7 @@ namespace NadekoBot.Modules.Administration
{ {
var channel = (ITextChannel)umsg.Channel; var channel = (ITextChannel)umsg.Channel;
var ch = await channel.Guild.CreateVoiceChannelAsync(channelName).ConfigureAwait(false); var ch = await channel.Guild.CreateVoiceChannelAsync(channelName).ConfigureAwait(false);
await channel.SendMessageAsync($"✅ Created voice channel **{ch.Name}**. ID: `{ch.Id}`").ConfigureAwait(false); await channel.SendConfirmAsync($"✅ Created voice channel **{ch.Name}**. ID: `{ch.Id}`").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -596,7 +425,7 @@ namespace NadekoBot.Modules.Administration
public async Task DelTxtChanl(IUserMessage umsg, [Remainder] ITextChannel toDelete) public async Task DelTxtChanl(IUserMessage umsg, [Remainder] ITextChannel toDelete)
{ {
await toDelete.DeleteAsync().ConfigureAwait(false); await toDelete.DeleteAsync().ConfigureAwait(false);
await umsg.Channel.SendMessageAsync($"🗑 Removed text channel **{toDelete.Name}**. ID: `{toDelete.Id}`").ConfigureAwait(false); await umsg.Channel.SendConfirmAsync($"🗑 Removed text channel **{toDelete.Name}**. ID: `{toDelete.Id}`").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -606,7 +435,7 @@ namespace NadekoBot.Modules.Administration
{ {
var channel = (ITextChannel)umsg.Channel; var channel = (ITextChannel)umsg.Channel;
var txtCh = await channel.Guild.CreateTextChannelAsync(channelName).ConfigureAwait(false); var txtCh = await channel.Guild.CreateTextChannelAsync(channelName).ConfigureAwait(false);
await channel.SendMessageAsync($"✅ Added text channel **{txtCh.Name}**. ID: `{txtCh.Id}`").ConfigureAwait(false); await channel.SendConfirmAsync($"✅ Added text channel **{txtCh.Name}**. ID: `{txtCh.Id}`").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -617,7 +446,7 @@ namespace NadekoBot.Modules.Administration
var channel = (ITextChannel)umsg.Channel; var channel = (ITextChannel)umsg.Channel;
topic = topic ?? ""; topic = topic ?? "";
await channel.ModifyAsync(c => c.Topic = topic); await channel.ModifyAsync(c => c.Topic = topic);
await channel.SendMessageAsync("🆗 **New channel topic set.**").ConfigureAwait(false); await channel.SendConfirmAsync("🆗 **New channel topic set.**").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -628,7 +457,7 @@ namespace NadekoBot.Modules.Administration
var channel = (ITextChannel)umsg.Channel; var channel = (ITextChannel)umsg.Channel;
await channel.ModifyAsync(c => c.Name = name).ConfigureAwait(false); await channel.ModifyAsync(c => c.Name = name).ConfigureAwait(false);
await channel.SendMessageAsync("🆗 **New channel name set.**").ConfigureAwait(false); await channel.SendConfirmAsync("🆗 **New channel name set.**").ConfigureAwait(false);
} }
@ -643,7 +472,7 @@ namespace NadekoBot.Modules.Administration
var enumerable = (await umsg.Channel.GetMessagesAsync()).AsEnumerable(); var enumerable = (await umsg.Channel.GetMessagesAsync()).AsEnumerable();
enumerable = enumerable.Where(x => x.Author.Id == user.Id); enumerable = enumerable.Where(x => x.Author.Id == user.Id);
await umsg.Channel.DeleteMessagesAsync(enumerable); await umsg.Channel.DeleteMessagesAsync(enumerable).ConfigureAwait(false);
} }
// prune x // prune x
@ -656,7 +485,7 @@ namespace NadekoBot.Modules.Administration
await (msg as IUserMessage).DeleteAsync(); await (msg as IUserMessage).DeleteAsync();
int limit = (count < 100) ? count : 100; int limit = (count < 100) ? count : 100;
var enumerable = (await msg.Channel.GetMessagesAsync(limit: limit)); var enumerable = (await msg.Channel.GetMessagesAsync(limit: limit));
await msg.Channel.DeleteMessagesAsync(enumerable); await msg.Channel.DeleteMessagesAsync(enumerable).ConfigureAwait(false);
} }
//prune @user [x] //prune @user [x]
@ -668,42 +497,34 @@ namespace NadekoBot.Modules.Administration
var channel = (ITextChannel)msg.Channel; var channel = (ITextChannel)msg.Channel;
int limit = (count < 100) ? count : 100; int limit = (count < 100) ? count : 100;
var enumerable = (await msg.Channel.GetMessagesAsync(limit: limit)).Where(m => m.Author == user); var enumerable = (await msg.Channel.GetMessagesAsync(limit: limit)).Where(m => m.Author == user);
await msg.Channel.DeleteMessagesAsync(enumerable); await msg.Channel.DeleteMessagesAsync(enumerable).ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[OwnerOnly] [OwnerOnly]
public async Task Die(IUserMessage umsg) public async Task Die(IUserMessage umsg)
{ {
var channel = (ITextChannel)umsg.Channel; try { await umsg.Channel.SendConfirmAsync(" **Shutting down.**").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
try { await channel.SendMessageAsync(" **Shutting down.**").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
await Task.Delay(2000).ConfigureAwait(false); await Task.Delay(2000).ConfigureAwait(false);
Environment.Exit(0); Environment.Exit(0);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[OwnerOnly] [OwnerOnly]
public async Task Setname(IUserMessage umsg, [Remainder] string newName) public async Task SetName(IUserMessage umsg, [Remainder] string newName)
{ {
var channel = (ITextChannel)umsg.Channel;
if (string.IsNullOrWhiteSpace(newName)) if (string.IsNullOrWhiteSpace(newName))
return; return;
await (await NadekoBot.Client.GetCurrentUserAsync()).ModifyAsync(u => u.Username = newName).ConfigureAwait(false); await (await NadekoBot.Client.GetCurrentUserAsync()).ModifyAsync(u => u.Username = newName).ConfigureAwait(false);
await channel.SendMessageAsync($" Successfully changed name to **{newName}**").ConfigureAwait(false); await umsg.Channel.SendConfirmAsync($" Successfully changed name to **{newName}**").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[OwnerOnly] [OwnerOnly]
public async Task SetAvatar(IUserMessage umsg, [Remainder] string img = null) public async Task SetAvatar(IUserMessage umsg, [Remainder] string img = null)
{ {
var channel = (ITextChannel)umsg.Channel;
if (string.IsNullOrWhiteSpace(img)) if (string.IsNullOrWhiteSpace(img))
return; return;
@ -719,44 +540,35 @@ namespace NadekoBot.Modules.Administration
} }
} }
await channel.SendMessageAsync("🆒 **New avatar set.**").ConfigureAwait(false); await umsg.Channel.SendConfirmAsync("🆒 **New avatar set.**").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[OwnerOnly] [OwnerOnly]
public async Task SetGame(IUserMessage umsg, [Remainder] string game = null) public async Task SetGame(IUserMessage umsg, [Remainder] string game = null)
{ {
var channel = (ITextChannel)umsg.Channel;
game = game ?? ""; game = game ?? "";
await NadekoBot.Client.SetGame(game).ConfigureAwait(false); await NadekoBot.Client.SetGame(game).ConfigureAwait(false);
await channel.SendMessageAsync("👾 **New game set.**").ConfigureAwait(false); await umsg.Channel.SendConfirmAsync("👾 **New game set.**").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[OwnerOnly] [OwnerOnly]
public async Task SetStream(IUserMessage umsg, string url, [Remainder] string name = null) public async Task SetStream(IUserMessage umsg, string url, [Remainder] string name = null)
{ {
var channel = (ITextChannel)umsg.Channel;
name = name ?? ""; name = name ?? "";
await NadekoBot.Client.SetStream(name, url).ConfigureAwait(false); await NadekoBot.Client.SetStream(name, url).ConfigureAwait(false);
await channel.SendMessageAsync(" **New stream set.**").ConfigureAwait(false); await umsg.Channel.SendConfirmAsync(" **New stream set.**").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[OwnerOnly] [OwnerOnly]
public async Task Send(IUserMessage umsg, string where, [Remainder] string msg = null) public async Task Send(IUserMessage umsg, string where, [Remainder] string msg = null)
{ {
var channel = (ITextChannel)umsg.Channel;
if (string.IsNullOrWhiteSpace(msg)) if (string.IsNullOrWhiteSpace(msg))
return; return;
@ -791,25 +603,24 @@ namespace NadekoBot.Modules.Administration
} }
else else
{ {
await channel.SendMessageAsync("⚠️ Invalid format.").ConfigureAwait(false); await umsg.Channel.SendErrorAsync("⚠️ Invalid format.").ConfigureAwait(false);
} }
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[OwnerOnly] [OwnerOnly]
public async Task Announce(IUserMessage umsg, [Remainder] string message) public async Task Announce(IUserMessage umsg, [Remainder] string message)
{ {
var channel = (ITextChannel)umsg.Channel; var channels = await Task.WhenAll(NadekoBot.Client.GetGuilds().Select(g =>
var channels = await Task.WhenAll(_client.GetGuilds().Select(g =>
g.GetDefaultChannelAsync() g.GetDefaultChannelAsync()
)).ConfigureAwait(false); )).ConfigureAwait(false);
await Task.WhenAll(channels.Select(c => c.SendMessageAsync($"🆕 **Message from {umsg.Author} `(Bot Owner)`:** " + message))) if (channels == null)
return;
await Task.WhenAll(channels.Where(c => c != null).Select(c => c.SendConfirmAsync($"🆕 Message from {umsg.Author} `[Bot Owner]`:", message)))
.ConfigureAwait(false); .ConfigureAwait(false);
await channel.SendMessageAsync("🆗").ConfigureAwait(false); await umsg.Channel.SendConfirmAsync("🆗").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -839,8 +650,10 @@ namespace NadekoBot.Modules.Administration
cnt -= 100; cnt -= 100;
} }
var title = $"Chatlog-{channel.Guild.Name}/#{channel.Name}-{DateTime.Now}.txt"; var title = $"Chatlog-{channel.Guild.Name}/#{channel.Name}-{DateTime.Now}.txt";
var grouping = msgs.GroupBy(x => $"{x.CreatedAt.Date:dd.MM.yyyy}")
.Select(g => new { date = g.Key, messages = g.OrderBy(x=>x.CreatedAt).Select(s => $"【{s.Timestamp:HH:mm:ss}】{s.Author}:" + s.ToString()) });
await (umsg.Author as IGuildUser).SendFileAsync( await (umsg.Author as IGuildUser).SendFileAsync(
await JsonConvert.SerializeObject(new { Messages = msgs.Select(s => $"【{s.Timestamp:HH:mm:ss}】{s.Author}:" + s.ToString()) }, Formatting.Indented).ToStream().ConfigureAwait(false), await JsonConvert.SerializeObject(grouping, Formatting.Indented).ToStream().ConfigureAwait(false),
title, title).ConfigureAwait(false); title, title).ConfigureAwait(false);
} }
@ -852,7 +665,7 @@ namespace NadekoBot.Modules.Administration
{ {
var channel = (ITextChannel)umsg.Channel; var channel = (ITextChannel)umsg.Channel;
string send = $"❕{umsg.Author.Mention} __`has invoked a mention on the following roles`__❕"; string send = $"❕{umsg.Author.Mention} has invoked a mention on the following roles ❕";
foreach (var role in roles) foreach (var role in roles)
{ {
send += $"\n**{role.Name}**\n"; send += $"\n**{role.Name}**\n";
@ -872,19 +685,15 @@ namespace NadekoBot.Modules.Administration
IGuild nadekoSupportServer; IGuild nadekoSupportServer;
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Donators(IUserMessage umsg) public async Task Donators(IUserMessage umsg)
{ {
var channel = (ITextChannel)umsg.Channel;
IEnumerable<Donator> donatorsOrdered; IEnumerable<Donator> donatorsOrdered;
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
donatorsOrdered = uow.Donators.GetDonatorsOrdered(); donatorsOrdered = uow.Donators.GetDonatorsOrdered();
} }
await umsg.Channel.SendConfirmAsync("Thanks to the people listed below for making this project happen!", string.Join("⭐", donatorsOrdered.Select(d => d.Name))).ConfigureAwait(false);
string str = $"**Thanks to the people listed below for making this project happen!**\n";
await channel.SendMessageAsync(str + string.Join("⭐", donatorsOrdered.Select(d => d.Name))).ConfigureAwait(false);
nadekoSupportServer = nadekoSupportServer ?? NadekoBot.Client.GetGuild(117523346618318850); nadekoSupportServer = nadekoSupportServer ?? NadekoBot.Client.GetGuild(117523346618318850);
@ -896,17 +705,14 @@ namespace NadekoBot.Modules.Administration
return; return;
var usrs = nadekoSupportServer.GetUsers().Where(u => u.Roles.Contains(patreonRole)); var usrs = nadekoSupportServer.GetUsers().Where(u => u.Roles.Contains(patreonRole));
await channel.SendMessageAsync("\n`Patreon supporters:`\n" + string.Join("⭐", usrs.Select(d => d.Username))).ConfigureAwait(false); await umsg.Channel.SendConfirmAsync("Patreon supporters", string.Join("⭐", usrs.Select(d => d.Username))).ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[OwnerOnly] [OwnerOnly]
public async Task Donadd(IUserMessage umsg, IUser donator, int amount) public async Task Donadd(IUserMessage umsg, IUser donator, int amount)
{ {
var channel = (ITextChannel)umsg.Channel;
Donator don; Donator don;
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
@ -914,7 +720,7 @@ namespace NadekoBot.Modules.Administration
await uow.CompleteAsync(); await uow.CompleteAsync();
} }
await channel.SendMessageAsync($"Successfuly added a new donator. Total donated amount from this user: {don.Amount} 👑").ConfigureAwait(false); await umsg.Channel.SendConfirmAsync($"Successfuly added a new donator. Total donated amount from this user: {don.Amount} 👑").ConfigureAwait(false);
} }
} }
} }

View File

@ -1,6 +1,7 @@
using Discord; using Discord;
using Discord.Commands; using Discord.Commands;
using NadekoBot.Attributes; using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NLog; using NLog;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
@ -74,26 +75,24 @@ namespace NadekoBot.Modules.Administration
private static ConcurrentDictionary<ulong, AntiSpamSetting> antiSpamGuilds = private static ConcurrentDictionary<ulong, AntiSpamSetting> antiSpamGuilds =
new ConcurrentDictionary<ulong, AntiSpamSetting>(); new ConcurrentDictionary<ulong, AntiSpamSetting>();
private Logger _log { get; } private static Logger _log { get; }
public AntiRaidCommands(ShardedDiscordClient client) static AntiRaidCommands()
{ {
_log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
client.MessageReceived += (imsg) => NadekoBot.Client.MessageReceived += async (imsg) =>
{
try
{ {
var msg = imsg as IUserMessage; var msg = imsg as IUserMessage;
if (msg == null || msg.Author.IsBot) if (msg == null || msg.Author.IsBot)
return Task.CompletedTask; return;
var channel = msg.Channel as ITextChannel; var channel = msg.Channel as ITextChannel;
if (channel == null) if (channel == null)
return Task.CompletedTask; return;
var t = Task.Run(async () =>
{
try
{
AntiSpamSetting spamSettings; AntiSpamSetting spamSettings;
if (!antiSpamGuilds.TryGetValue(channel.Guild.Id, out spamSettings)) if (!antiSpamGuilds.TryGetValue(channel.Guild.Id, out spamSettings))
return; return;
@ -105,27 +104,23 @@ namespace NadekoBot.Modules.Administration
{ {
if (spamSettings.UserStats.TryRemove(msg.Author.Id, out stats)) if (spamSettings.UserStats.TryRemove(msg.Author.Id, out stats))
{ {
await PunishUsers(spamSettings.Action, await GetMuteRole(channel.Guild), ProtectionType.Spamming, (IGuildUser)msg.Author) await PunishUsers(spamSettings.Action, ProtectionType.Spamming, (IGuildUser)msg.Author)
.ConfigureAwait(false); .ConfigureAwait(false);
} }
} }
} }
catch { } catch { }
});
return Task.CompletedTask;
}; };
client.UserJoined += (usr) => NadekoBot.Client.UserJoined += async (usr) =>
{
try
{ {
if (usr.IsBot) if (usr.IsBot)
return Task.CompletedTask; return;
AntiRaidSetting settings; AntiRaidSetting settings;
if (!antiRaidGuilds.TryGetValue(usr.Guild.Id, out settings)) if (!antiRaidGuilds.TryGetValue(usr.Guild.Id, out settings))
return Task.CompletedTask; return;
var t = Task.Run(async () =>
{
if (!settings.RaidUsers.Add(usr)) if (!settings.RaidUsers.Add(usr))
return; return;
@ -136,19 +131,19 @@ namespace NadekoBot.Modules.Administration
var users = settings.RaidUsers.ToArray(); var users = settings.RaidUsers.ToArray();
settings.RaidUsers.Clear(); settings.RaidUsers.Clear();
await PunishUsers(settings.Action, await GetMuteRole(usr.Guild), ProtectionType.Raiding, users).ConfigureAwait(false); await PunishUsers(settings.Action, ProtectionType.Raiding, users).ConfigureAwait(false);
} }
await Task.Delay(1000 * settings.Seconds).ConfigureAwait(false); await Task.Delay(1000 * settings.Seconds).ConfigureAwait(false);
settings.RaidUsers.TryRemove(usr); settings.RaidUsers.TryRemove(usr);
--settings.UsersCount; --settings.UsersCount;
});
return Task.CompletedTask; }
catch { }
}; };
} }
private async Task PunishUsers(PunishmentAction action, IRole muteRole, ProtectionType pt, params IGuildUser[] gus) private static async Task PunishUsers(PunishmentAction action, ProtectionType pt, params IGuildUser[] gus)
{ {
foreach (var gu in gus) foreach (var gu in gus)
{ {
@ -157,21 +152,21 @@ namespace NadekoBot.Modules.Administration
case PunishmentAction.Mute: case PunishmentAction.Mute:
try try
{ {
await gu.AddRolesAsync(muteRole); await MuteCommands.Mute(gu).ConfigureAwait(false);
} }
catch (Exception ex) { _log.Warn(ex, "I can't apply punishement"); } catch (Exception ex) { _log.Warn(ex, "I can't apply punishement"); }
break; break;
case PunishmentAction.Kick: case PunishmentAction.Kick:
try try
{ {
await gu.Guild.AddBanAsync(gu, 7); await gu.Guild.AddBanAsync(gu, 7).ConfigureAwait(false);
try try
{ {
await gu.Guild.RemoveBanAsync(gu); await gu.Guild.RemoveBanAsync(gu).ConfigureAwait(false);
} }
catch catch
{ {
await gu.Guild.RemoveBanAsync(gu); await gu.Guild.RemoveBanAsync(gu).ConfigureAwait(false);
// try it twice, really don't want to ban user if // try it twice, really don't want to ban user if
// only kick has been specified as the punishement // only kick has been specified as the punishement
} }
@ -181,7 +176,7 @@ namespace NadekoBot.Modules.Administration
case PunishmentAction.Ban: case PunishmentAction.Ban:
try try
{ {
await gu.Guild.AddBanAsync(gu, 7); await gu.Guild.AddBanAsync(gu, 7).ConfigureAwait(false);
} }
catch (Exception ex) { _log.Warn(ex, "I can't apply punishment"); } catch (Exception ex) { _log.Warn(ex, "I can't apply punishment"); }
break; break;
@ -202,23 +197,23 @@ namespace NadekoBot.Modules.Administration
if (userThreshold < 2 || userThreshold > 30) if (userThreshold < 2 || userThreshold > 30)
{ {
await channel.SendMessageAsync("❗User threshold must be between **2** and **30**.").ConfigureAwait(false); await channel.SendErrorAsync("❗User threshold must be between **2** and **30**.").ConfigureAwait(false);
return; return;
} }
if (seconds < 2 || seconds > 300) if (seconds < 2 || seconds > 300)
{ {
await channel.SendMessageAsync("❗Time must be between **2** and **300** seconds.").ConfigureAwait(false); await channel.SendErrorAsync("❗Time must be between **2** and **300** seconds.").ConfigureAwait(false);
return; return;
} }
try try
{ {
await GetMuteRole(channel.Guild).ConfigureAwait(false); await MuteCommands.GetMuteRole(channel.Guild).ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {
await channel.SendMessageAsync("⚠️ Failed creating a mute role. Give me ManageRoles permission" + await channel.SendConfirmAsync("⚠️ Failed creating a mute role. Give me ManageRoles permission" +
"or create 'nadeko-mute' role with disabled SendMessages and try again.") "or create 'nadeko-mute' role with disabled SendMessages and try again.")
.ConfigureAwait(false); .ConfigureAwait(false);
_log.Warn(ex); _log.Warn(ex);
@ -233,7 +228,7 @@ namespace NadekoBot.Modules.Administration
}; };
antiRaidGuilds.AddOrUpdate(channel.Guild.Id, setting, (id, old) => setting); antiRaidGuilds.AddOrUpdate(channel.Guild.Id, setting, (id, old) => setting);
await channel.SendMessageAsync($" {imsg.Author.Mention} If **{userThreshold}** or more users join within **{seconds}** seconds, I will **{action}** them.") await channel.SendConfirmAsync($" {imsg.Author.Mention} If **{userThreshold}** or more users join within **{seconds}** seconds, I will **{action}** them.")
.ConfigureAwait(false); .ConfigureAwait(false);
} }
@ -250,17 +245,17 @@ namespace NadekoBot.Modules.Administration
AntiSpamSetting throwaway; AntiSpamSetting throwaway;
if (antiSpamGuilds.TryRemove(channel.Guild.Id, out throwaway)) if (antiSpamGuilds.TryRemove(channel.Guild.Id, out throwaway))
{ {
await channel.SendMessageAsync("🆗 **Anti-Spam feature** has been **disabled** on this server.").ConfigureAwait(false); await channel.SendConfirmAsync("🆗 **Anti-Spam feature** has been **disabled** on this server.").ConfigureAwait(false);
} }
else else
{ {
try try
{ {
await GetMuteRole(channel.Guild).ConfigureAwait(false); await MuteCommands.GetMuteRole(channel.Guild).ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {
await channel.SendMessageAsync("⚠️ Failed creating a mute role. Give me ManageRoles permission" + await channel.SendErrorAsync("⚠️ Failed creating a mute role. Give me ManageRoles permission" +
"or create 'nadeko-mute' role with disabled SendMessages and try again.") "or create 'nadeko-mute' role with disabled SendMessages and try again.")
.ConfigureAwait(false); .ConfigureAwait(false);
_log.Warn(ex); _log.Warn(ex);
@ -272,7 +267,7 @@ namespace NadekoBot.Modules.Administration
Action = action, Action = action,
MessageThreshold = messageCount, MessageThreshold = messageCount,
})) }))
await channel.SendMessageAsync("✅ **Anti-Spam feature** has been **enabled** on this server.").ConfigureAwait(false); await channel.SendConfirmAsync("✅ **Anti-Spam feature** has been **enabled** on this server.").ConfigureAwait(false);
} }
} }

View File

@ -1,10 +1,12 @@
using Discord; using Discord;
using Discord.Commands; using Discord.Commands;
using NadekoBot.Attributes; using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Services; using NadekoBot.Services;
using NadekoBot.Services.Database.Models; using NadekoBot.Services.Database.Models;
using NLog; using NLog;
using System; using System;
using System.Collections.Concurrent;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -15,35 +17,31 @@ namespace NadekoBot.Modules.Administration
[Group] [Group]
public class AutoAssignRoleCommands public class AutoAssignRoleCommands
{ {
private Logger _log { get; } private static Logger _log { get; }
//guildid/roleid
private static ConcurrentDictionary<ulong, ulong> AutoAssignedRoles { get; }
public AutoAssignRoleCommands() static AutoAssignRoleCommands()
{ {
var _client = NadekoBot.Client; AutoAssignedRoles = new ConcurrentDictionary<ulong, ulong>(NadekoBot.AllGuildConfigs.Where(x => x.AutoAssignRoleId != 0)
this._log = LogManager.GetCurrentClassLogger(); .ToDictionary(k => k.GuildId, v => v.AutoAssignRoleId));
_client.UserJoined += (user) => _log = LogManager.GetCurrentClassLogger();
{ NadekoBot.Client.UserJoined += async (user) =>
var t = Task.Run(async () =>
{ {
try try
{ {
GuildConfig conf; ulong roleId = 0;
using (var uow = DbHandler.UnitOfWork()) AutoAssignedRoles.TryGetValue(user.Guild.Id, out roleId);
{
conf = uow.GuildConfigs.For(user.Guild.Id);
}
if (conf.AutoAssignRoleId == 0) if (roleId == 0)
return; return;
var role = user.Guild.Roles.FirstOrDefault(r => r.Id == conf.AutoAssignRoleId); var role = user.Guild.Roles.FirstOrDefault(r => r.Id == roleId);
if (role != null) if (role != null)
await user.AddRolesAsync(role); await user.AddRolesAsync(role).ConfigureAwait(false);
} }
catch (Exception ex) { _log.Warn(ex); } catch (Exception ex) { _log.Warn(ex); }
});
return Task.CompletedTask;
}; };
} }
@ -57,23 +55,29 @@ namespace NadekoBot.Modules.Administration
GuildConfig conf; GuildConfig conf;
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
conf = uow.GuildConfigs.For(channel.Guild.Id); conf = uow.GuildConfigs.For(channel.Guild.Id, set => set);
if (role == null) if (role == null)
{
conf.AutoAssignRoleId = 0; conf.AutoAssignRoleId = 0;
ulong throwaway;
AutoAssignedRoles.TryRemove(channel.Guild.Id, out throwaway);
}
else else
{
conf.AutoAssignRoleId = role.Id; conf.AutoAssignRoleId = role.Id;
AutoAssignedRoles.AddOrUpdate(channel.Guild.Id, role.Id, (key, val) => role.Id);
}
uow.GuildConfigs.Update(conf);
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
} }
if (role == null) if (role == null)
{ {
await channel.SendMessageAsync("🆗 **Auto assign role** on user join is now **disabled**.").ConfigureAwait(false); await channel.SendConfirmAsync("🆗 **Auto assign role** on user join is now **disabled**.").ConfigureAwait(false);
return; return;
} }
await channel.SendMessageAsync("✅ **Auto assign role** on user join is now **enabled**.").ConfigureAwait(false); await channel.SendConfirmAsync("✅ **Auto assign role** on user join is now **enabled**.").ConfigureAwait(false);
} }
} }
} }

View File

@ -16,24 +16,21 @@ namespace NadekoBot.Modules.Administration
[Group] [Group]
public class CrossServerTextChannel public class CrossServerTextChannel
{ {
public CrossServerTextChannel() static CrossServerTextChannel()
{ {
_log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
NadekoBot.Client.MessageReceived += (imsg) => NadekoBot.Client.MessageReceived += async (imsg) =>
{
try
{ {
if (imsg.Author.IsBot) if (imsg.Author.IsBot)
return Task.CompletedTask; return;
var msg = imsg as IUserMessage; var msg = imsg as IUserMessage;
if (msg == null) if (msg == null)
return Task.CompletedTask; return;
var channel = imsg.Channel as ITextChannel; var channel = imsg.Channel as ITextChannel;
if (channel == null) if (channel == null)
return Task.CompletedTask; return;
Task.Run(async () =>
{
if (msg.Author.Id == NadekoBot.Client.GetCurrentUser().Id) return; if (msg.Author.Id == NadekoBot.Client.GetCurrentUser().Id) return;
foreach (var subscriber in Subscribers) foreach (var subscriber in Subscribers)
{ {
@ -45,16 +42,18 @@ namespace NadekoBot.Modules.Administration
try { await chan.SendMessageAsync(GetText(channel.Guild, channel, (IGuildUser)msg.Author, msg)).ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); } try { await chan.SendMessageAsync(GetText(channel.Guild, channel, (IGuildUser)msg.Author, msg)).ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
} }
} }
}); }
return Task.CompletedTask; catch (Exception ex) {
_log.Warn(ex);
}
}; };
} }
private string GetText(IGuild server, ITextChannel channel, IGuildUser user, IUserMessage message) => private static string GetText(IGuild server, ITextChannel channel, IGuildUser user, IUserMessage message) =>
$"**{server.Name} | {channel.Name}** `{user.Username}`: " + message.Content; $"**{server.Name} | {channel.Name}** `{user.Username}`: " + message.Content;
public static readonly ConcurrentDictionary<int, ConcurrentHashSet<ITextChannel>> Subscribers = new ConcurrentDictionary<int, ConcurrentHashSet<ITextChannel>>(); public static readonly ConcurrentDictionary<int, ConcurrentHashSet<ITextChannel>> Subscribers = new ConcurrentDictionary<int, ConcurrentHashSet<ITextChannel>>();
private Logger _log { get; } private static Logger _log { get; }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
@ -67,7 +66,7 @@ namespace NadekoBot.Modules.Administration
if (Subscribers.TryAdd(token, set)) if (Subscribers.TryAdd(token, set))
{ {
set.Add(channel); set.Add(channel);
await ((IGuildUser)msg.Author).SendMessageAsync("This is your CSC token:" + token.ToString()).ConfigureAwait(false); await ((IGuildUser)msg.Author).SendConfirmAsync("This is your CSC token", token.ToString()).ConfigureAwait(false);
} }
} }
@ -82,7 +81,7 @@ namespace NadekoBot.Modules.Administration
if (!Subscribers.TryGetValue(token, out set)) if (!Subscribers.TryGetValue(token, out set))
return; return;
set.Add(channel); set.Add(channel);
await channel.SendMessageAsync(":ok:").ConfigureAwait(false); await channel.SendConfirmAsync("Joined cross server channel.").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -96,7 +95,7 @@ namespace NadekoBot.Modules.Administration
{ {
subscriber.Value.TryRemove(channel); subscriber.Value.TryRemove(channel);
} }
await channel.SendMessageAsync(":ok:").ConfigureAwait(false); await channel.SendMessageAsync("Left cross server channel.").ConfigureAwait(false);
} }
} }
} }

View File

@ -1,6 +1,7 @@
using Discord; using Discord;
using Discord.Commands; using Discord.Commands;
using NadekoBot.Attributes; using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Services; using NadekoBot.Services;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@ -39,9 +40,9 @@ namespace NadekoBot.Modules.Administration
uow.Complete(); uow.Complete();
} }
if (ForwardDMs) if (ForwardDMs)
await channel.SendMessageAsync("✅ **I will forward DMs from now on.**").ConfigureAwait(false); await channel.SendConfirmAsync("✅ **I will forward DMs from now on.**").ConfigureAwait(false);
else else
await channel.SendMessageAsync("🆗 **I will stop forwarding DMs from now on.**").ConfigureAwait(false); await channel.SendConfirmAsync("🆗 **I will stop forwarding DMs from now on.**").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -57,9 +58,9 @@ namespace NadekoBot.Modules.Administration
uow.Complete(); uow.Complete();
} }
if (ForwardDMsToAllOwners) if (ForwardDMsToAllOwners)
await channel.SendMessageAsync(" **I will forward DMs to all owners.**").ConfigureAwait(false); await channel.SendConfirmAsync(" **I will forward DMs to all owners.**").ConfigureAwait(false);
else else
await channel.SendMessageAsync(" **I will forward DMs only to the first owner.**").ConfigureAwait(false); await channel.SendConfirmAsync(" **I will forward DMs only to the first owner.**").ConfigureAwait(false);
} }
@ -67,17 +68,17 @@ namespace NadekoBot.Modules.Administration
{ {
if (ForwardDMs && ownerChannels.Any()) if (ForwardDMs && ownerChannels.Any())
{ {
var toSend = $"```markdown\n I received a message from [{msg.Author}]({msg.Author.Id}): {msg.Content}```"; var title = $"DM from [{msg.Author}]({msg.Author.Id})";
if (ForwardDMsToAllOwners) if (ForwardDMsToAllOwners)
{ {
var msgs = await Task.WhenAll(ownerChannels.Where(ch => ch.Recipient.Id != msg.Author.Id) var msgs = await Task.WhenAll(ownerChannels.Where(ch => ch.Recipient.Id != msg.Author.Id)
.Select(ch => ch.SendMessageAsync(toSend))).ConfigureAwait(false); .Select(ch => ch.SendConfirmAsync(title, msg.Content))).ConfigureAwait(false);
} }
else else
{ {
var firstOwnerChannel = ownerChannels.First(); var firstOwnerChannel = ownerChannels.First();
if (firstOwnerChannel.Recipient.Id != msg.Author.Id) if (firstOwnerChannel.Recipient.Id != msg.Author.Id)
try { await firstOwnerChannel.SendMessageAsync(toSend).ConfigureAwait(false); } catch { } try { await firstOwnerChannel.SendConfirmAsync(title, msg.Content).ConfigureAwait(false); } catch { }
} }
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -2,6 +2,7 @@
using Discord.Commands; using Discord.Commands;
using Discord.WebSocket; using Discord.WebSocket;
using NadekoBot.Attributes; using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Services; using NadekoBot.Services;
using NadekoBot.Services.Database.Models; using NadekoBot.Services.Database.Models;
using NLog; using NLog;
@ -18,7 +19,7 @@ namespace NadekoBot.Modules.Administration
[Group] [Group]
public class RepeatCommands public class RepeatCommands
{ {
public ConcurrentDictionary<ulong, RepeatRunner> repeaters; public static ConcurrentDictionary<ulong, RepeatRunner> repeaters { get; }
public class RepeatRunner public class RepeatRunner
{ {
@ -49,10 +50,16 @@ namespace NadekoBot.Modules.Administration
{ {
while (!token.IsCancellationRequested) while (!token.IsCancellationRequested)
{ {
var toSend = "🔄 " + Repeater.Message;
await Task.Delay(Repeater.Interval, token).ConfigureAwait(false); await Task.Delay(Repeater.Interval, token).ConfigureAwait(false);
//var lastMsgInChannel = (await Channel.GetMessagesAsync(2)).FirstOrDefault();
// if (lastMsgInChannel.Id == oldMsg?.Id) //don't send if it's the same message in the channel
// continue;
if (oldMsg != null) if (oldMsg != null)
try { await oldMsg.DeleteAsync(); } catch { } try { await oldMsg.DeleteAsync(); } catch { }
try { oldMsg = await Channel.SendMessageAsync("🔄 " + Repeater.Message).ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); try { source.Cancel(); } catch { } } try { oldMsg = await Channel.SendMessageAsync(toSend).ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
} }
} }
catch (OperationCanceledException) { } catch (OperationCanceledException) { }
@ -70,7 +77,7 @@ namespace NadekoBot.Modules.Administration
} }
} }
public RepeatCommands() static RepeatCommands()
{ {
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
@ -88,7 +95,7 @@ namespace NadekoBot.Modules.Administration
RepeatRunner rep; RepeatRunner rep;
if (!repeaters.TryGetValue(channel.Id, out rep)) if (!repeaters.TryGetValue(channel.Id, out rep))
{ {
await channel.SendMessageAsync(" **No repeating message found on this server.**").ConfigureAwait(false); await channel.SendErrorAsync(" **No repeating message found on this server.**").ConfigureAwait(false);
return; return;
} }
rep.Reset(); rep.Reset();
@ -110,10 +117,10 @@ namespace NadekoBot.Modules.Administration
await uow.CompleteAsync(); await uow.CompleteAsync();
} }
rep.Stop(); rep.Stop();
await channel.SendMessageAsync("✅ **Stopped repeating a message.**").ConfigureAwait(false); await channel.SendConfirmAsync("✅ **Stopped repeating a message.**").ConfigureAwait(false);
} }
else else
await channel.SendMessageAsync(" **No message is repeating.**").ConfigureAwait(false); await channel.SendConfirmAsync(" **No message is repeating.**").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -159,7 +166,7 @@ namespace NadekoBot.Modules.Administration
return old; return old;
}); });
await channel.SendMessageAsync($"🔁 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 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);
} }
} }
} }

View File

@ -109,7 +109,7 @@ namespace NadekoBot.Modules.Administration
var byeMsg = (string)reader["ByeText"]; var byeMsg = (string)reader["ByeText"];
var grdel = false; var grdel = false;
var byedel = grdel; var byedel = grdel;
var gc = uow.GuildConfigs.For(gid); var gc = uow.GuildConfigs.For(gid, set => set);
if (greetDM) if (greetDM)
gc.SendDmGreetMessage = greet; gc.SendDmGreetMessage = greet;
@ -195,12 +195,9 @@ namespace NadekoBot.Modules.Administration
guildConfig.GenerateCurrencyChannelIds = new HashSet<GCChannelId>(data.GenerateCurrencyChannels.Select(gc => new GCChannelId() { ChannelId = gc.Key })); 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()); selfAssRoles.AddRange(data.ListOfSelfAssignableRoles.Select(r => new SelfAssignedRole() { GuildId = guildConfig.GuildId, RoleId = r }).ToArray());
var logSetting = guildConfig.LogSetting; var logSetting = guildConfig.LogSetting;
guildConfig.LogSetting.IsLogging = data.LogChannel != null;
guildConfig.LogSetting.ChannelId = data.LogChannel ?? 0;
guildConfig.LogSetting.IgnoredChannels = new HashSet<IgnoredLogChannel>(data.LogserverIgnoreChannels.Select(id => new IgnoredLogChannel() { ChannelId = id })); guildConfig.LogSetting.IgnoredChannels = new HashSet<IgnoredLogChannel>(data.LogserverIgnoreChannels.Select(id => new IgnoredLogChannel() { ChannelId = id }));
guildConfig.LogSetting.LogUserPresence = data.LogPresenceChannel != null; guildConfig.LogSetting.LogUserPresenceId = data.LogPresenceChannel;
guildConfig.LogSetting.UserPresenceChannelId = data.LogPresenceChannel ?? 0;
guildConfig.FollowedStreams = new HashSet<FollowedStream>(data.ObservingStreams.Select(x => guildConfig.FollowedStreams = new HashSet<FollowedStream>(data.ObservingStreams.Select(x =>

View File

@ -0,0 +1,287 @@
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using Microsoft.EntityFrameworkCore;
using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Services;
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.Administration
{
public partial class Administration
{
[Group]
public class MuteCommands
{
private static ConcurrentDictionary<ulong, string> GuildMuteRoles { get; } = new ConcurrentDictionary<ulong, string>();
private static ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>> MutedUsers { get; } = new ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>>();
public static event Action<IGuildUser, MuteType> UserMuted = delegate { };
public static event Action<IGuildUser, MuteType> UserUnmuted = delegate { };
public enum MuteType {
Voice,
Chat,
All
}
static MuteCommands() {
using (var uow = DbHandler.UnitOfWork())
{
var configs = NadekoBot.AllGuildConfigs;
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(
k => k.GuildId,
v => new ConcurrentHashSet<ulong>(v.MutedUsers.Select(m => m.UserId))
));
}
NadekoBot.Client.UserJoined += Client_UserJoined;
}
private static async void Client_UserJoined(IGuildUser usr)
{
try
{
ConcurrentHashSet<ulong> muted;
MutedUsers.TryGetValue(usr.Guild.Id, out muted);
if (muted == null || !muted.Contains(usr.Id))
return;
else
await Mute(usr).ConfigureAwait(false);
}
catch (Exception ex)
{
_log.Warn(ex);
}
}
public static async Task Mute(IGuildUser usr)
{
await usr.ModifyAsync(x => x.Mute = true).ConfigureAwait(false);
await usr.AddRolesAsync(await GetMuteRole(usr.Guild)).ConfigureAwait(false);
using (var uow = DbHandler.UnitOfWork())
{
var config = uow.GuildConfigs.For(usr.Guild.Id, set => set.Include(gc => gc.MutedUsers));
config.MutedUsers.Add(new MutedUserId()
{
UserId = usr.Id
});
ConcurrentHashSet<ulong> muted;
if (MutedUsers.TryGetValue(usr.Guild.Id, out muted))
muted.Add(usr.Id);
await uow.CompleteAsync().ConfigureAwait(false);
}
UserMuted(usr, MuteType.All);
}
public static async Task Unmute(IGuildUser usr)
{
await usr.ModifyAsync(x => x.Mute = false).ConfigureAwait(false);
await usr.RemoveRolesAsync(await GetMuteRole(usr.Guild)).ConfigureAwait(false);
using (var uow = DbHandler.UnitOfWork())
{
var config = uow.GuildConfigs.For(usr.Guild.Id, set => set.Include(gc => gc.MutedUsers));
config.MutedUsers.Remove(new MutedUserId()
{
UserId = usr.Id
});
ConcurrentHashSet<ulong> 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)
{
const string defaultMuteRoleName = "nadeko-mute";
var muteRoleName = GuildMuteRoles.GetOrAdd(guild.Id, defaultMuteRoleName);
var muteRole = guild.Roles.FirstOrDefault(r => r.Name == muteRoleName);
if (muteRole == null)
{
//if it doesn't exist, create it
try { muteRole = await guild.CreateRoleAsync(muteRoleName, GuildPermissions.None).ConfigureAwait(false); }
catch
{
//if creations fails, maybe the name is not correct, find default one, if doesn't work, create default one
muteRole = guild.Roles.FirstOrDefault(r => r.Name == muteRoleName) ??
await guild.CreateRoleAsync(defaultMuteRoleName, GuildPermissions.None).ConfigureAwait(false);
}
foreach (var toOverwrite in guild.GetTextChannels())
{
try
{
await toOverwrite.AddPermissionOverwriteAsync(muteRole, new OverwritePermissions(sendMessages: PermValue.Deny, attachFiles: PermValue.Deny))
.ConfigureAwait(false);
}
catch { }
await Task.Delay(200).ConfigureAwait(false);
}
}
return muteRole;
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[RequirePermission(GuildPermission.ManageRoles)]
[Priority(1)]
public async Task SetMuteRole(IUserMessage imsg, [Remainder] string name)
{
var channel = (ITextChannel)imsg.Channel;
name = name.Trim();
if (string.IsNullOrWhiteSpace(name))
return;
using (var uow = DbHandler.UnitOfWork())
{
var config = uow.GuildConfigs.For(channel.Guild.Id, set => set);
config.MuteRoleName = name;
GuildMuteRoles.AddOrUpdate(channel.Guild.Id, name, (id, old) => name);
await uow.CompleteAsync().ConfigureAwait(false);
}
await channel.SendConfirmAsync("☑️ **New mute role set.**").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[RequirePermission(GuildPermission.ManageRoles)]
[Priority(0)]
public Task SetMuteRole(IUserMessage imsg, [Remainder] IRole role)
=> SetMuteRole(imsg, role.Name);
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[RequirePermission(GuildPermission.ManageRoles)]
[RequirePermission(GuildPermission.MuteMembers)]
public async Task Mute(IUserMessage umsg, IGuildUser user)
{
var channel = (ITextChannel)umsg.Channel;
try
{
await Mute(user).ConfigureAwait(false);
await channel.SendConfirmAsync($"🔇 **{user}** has been **muted** from text and voice chat.").ConfigureAwait(false);
}
catch
{
await channel.SendErrorAsync("⚠️ I most likely don't have the permission necessary for that.").ConfigureAwait(false);
}
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[RequirePermission(GuildPermission.ManageRoles)]
[RequirePermission(GuildPermission.MuteMembers)]
public async Task Unmute(IUserMessage umsg, IGuildUser user)
{
var channel = (ITextChannel)umsg.Channel;
try
{
await Unmute(user).ConfigureAwait(false);
await channel.SendConfirmAsync($"🔉 **{user}** has been **unmuted** from text and voice chat.").ConfigureAwait(false);
}
catch
{
await channel.SendErrorAsync("⚠️ I most likely don't have the permission necessary for that.").ConfigureAwait(false);
}
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[RequirePermission(GuildPermission.ManageRoles)]
public async Task ChatMute(IUserMessage umsg, IGuildUser user)
{
var channel = (ITextChannel)umsg.Channel;
try
{
await user.AddRolesAsync(await GetMuteRole(channel.Guild).ConfigureAwait(false)).ConfigureAwait(false);
UserMuted(user, MuteType.Chat);
await channel.SendConfirmAsync($"✏️🚫 **{user}** has been **muted** from chatting.").ConfigureAwait(false);
}
catch
{
await channel.SendErrorAsync("⚠️ I most likely don't have the permission necessary for that.").ConfigureAwait(false);
}
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[RequirePermission(GuildPermission.ManageRoles)]
public async Task ChatUnmute(IUserMessage umsg, IGuildUser user)
{
var channel = (ITextChannel)umsg.Channel;
try
{
await user.RemoveRolesAsync(await GetMuteRole(channel.Guild).ConfigureAwait(false)).ConfigureAwait(false);
UserUnmuted(user, MuteType.Chat);
await channel.SendConfirmAsync($"✏️✅ **{user}** has been **unmuted** from chatting.").ConfigureAwait(false);
}
catch
{
await channel.SendErrorAsync("⚠️ I most likely don't have the permission necessary for that.").ConfigureAwait(false);
}
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[RequirePermission(GuildPermission.MuteMembers)]
public async Task VoiceMute(IUserMessage umsg, IGuildUser user)
{
var channel = (ITextChannel)umsg.Channel;
try
{
await user.ModifyAsync(usr => usr.Mute = true).ConfigureAwait(false);
UserMuted(user, MuteType.Voice);
await channel.SendConfirmAsync($"🎙🚫 **{user}** has been **voice muted**.").ConfigureAwait(false);
}
catch
{
await channel.SendErrorAsync("⚠️ I most likely don't have the permission necessary for that.").ConfigureAwait(false);
}
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[RequirePermission(GuildPermission.MuteMembers)]
public async Task VoiceUnmute(IUserMessage umsg, IGuildUser user)
{
var channel = (ITextChannel)umsg.Channel;
try
{
await user.ModifyAsync(usr => usr.Mute = false).ConfigureAwait(false);
UserUnmuted(user, MuteType.Voice);
await channel.SendConfirmAsync($"🎙✅ **{user}** has been **voice unmuted**.").ConfigureAwait(false);
}
catch
{
await channel.SendErrorAsync("⚠️ I most likely don't have the permission necessary for that.").ConfigureAwait(false);
}
}
}
}
}

View File

@ -18,7 +18,7 @@ namespace NadekoBot.Modules.Administration
[Group] [Group]
public class PlayingRotateCommands public class PlayingRotateCommands
{ {
private Logger _log { get; } private static Logger _log { get; }
public static List<PlayingStatus> RotatingStatusMessages { get; } public static List<PlayingStatus> RotatingStatusMessages { get; }
public static bool RotatingStatuses { get; private set; } = false; public static bool RotatingStatuses { get; private set; } = false;
@ -30,12 +30,9 @@ namespace NadekoBot.Modules.Administration
RotatingStatusMessages = conf.RotatingStatusMessages; RotatingStatusMessages = conf.RotatingStatusMessages;
RotatingStatuses = conf.RotatingStatuses; RotatingStatuses = conf.RotatingStatuses;
} }
}
public PlayingRotateCommands()
{
_log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
Task.Run(async () => var t = Task.Run(async () =>
{ {
var index = 0; var index = 0;
do do
@ -90,12 +87,9 @@ namespace NadekoBot.Modules.Administration
}; };
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[OwnerOnly] [OwnerOnly]
public async Task RotatePlaying(IUserMessage umsg) public async Task RotatePlaying(IUserMessage umsg)
{ {
var channel = (ITextChannel)umsg.Channel;
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
var config = uow.BotConfig.GetOrCreate(); var config = uow.BotConfig.GetOrCreate();
@ -104,18 +98,15 @@ namespace NadekoBot.Modules.Administration
await uow.CompleteAsync(); await uow.CompleteAsync();
} }
if (RotatingStatuses) if (RotatingStatuses)
await channel.SendMessageAsync("🆗 **Rotating playing status enabled.**"); await umsg.Channel.SendConfirmAsync("🆗 **Rotating playing status enabled.**").ConfigureAwait(false);
else else
await channel.SendMessageAsync(" **Rotating playing status disabled.**"); await umsg.Channel.SendConfirmAsync(" **Rotating playing status disabled.**").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[OwnerOnly] [OwnerOnly]
public async Task AddPlaying(IUserMessage umsg, [Remainder] string status) public async Task AddPlaying(IUserMessage umsg, [Remainder] string status)
{ {
var channel = (ITextChannel)umsg.Channel;
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
var config = uow.BotConfig.GetOrCreate(); var config = uow.BotConfig.GetOrCreate();
@ -125,33 +116,27 @@ namespace NadekoBot.Modules.Administration
await uow.CompleteAsync(); await uow.CompleteAsync();
} }
await channel.SendMessageAsync("✅ **Added.**").ConfigureAwait(false); await umsg.Channel.SendConfirmAsync("✅ **Added.**").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[OwnerOnly] [OwnerOnly]
public async Task ListPlaying(IUserMessage umsg) public async Task ListPlaying(IUserMessage umsg)
{ {
var channel = (ITextChannel)umsg.Channel;
if (!RotatingStatusMessages.Any()) if (!RotatingStatusMessages.Any())
await channel.SendMessageAsync("❎ **No rotating playing statuses set.**"); await umsg.Channel.SendErrorAsync("❎ **No rotating playing statuses set.**");
else else
{ {
var i = 1; var i = 1;
await channel.SendMessageAsync($" {umsg.Author.Mention} `Here is a list of rotating statuses:`\n\n\t" + string.Join("\n\t", RotatingStatusMessages.Select(rs => $"`{i++}.` {rs.Status}"))); await umsg.Channel.SendConfirmAsync($" {umsg.Author.Mention} `Here is a list of rotating statuses:`\n\n\t" + string.Join("\n\t", RotatingStatusMessages.Select(rs => $"`{i++}.` {rs.Status}")));
} }
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[OwnerOnly] [OwnerOnly]
public async Task RemovePlaying(IUserMessage umsg, int index) public async Task RemovePlaying(IUserMessage umsg, int index)
{ {
var channel = (ITextChannel)umsg.Channel;
index -= 1; index -= 1;
string msg = ""; string msg = "";
@ -166,7 +151,7 @@ namespace NadekoBot.Modules.Administration
RotatingStatusMessages.RemoveAt(index); RotatingStatusMessages.RemoveAt(index);
await uow.CompleteAsync(); await uow.CompleteAsync();
} }
await channel.SendMessageAsync($"🗑 **Removed the the playing message:** {msg}").ConfigureAwait(false); await umsg.Channel.SendConfirmAsync($"🗑 **Removed the the playing message:** {msg}").ConfigureAwait(false);
} }
} }
} }

View File

@ -16,9 +16,7 @@ namespace NadekoBot.Modules.Administration
public class RatelimitCommand public class RatelimitCommand
{ {
public static ConcurrentDictionary<ulong, Ratelimiter> RatelimitingChannels = new ConcurrentDictionary<ulong, Ratelimiter>(); public static ConcurrentDictionary<ulong, Ratelimiter> RatelimitingChannels = new ConcurrentDictionary<ulong, Ratelimiter>();
private Logger _log { get; } private static Logger _log { get; }
private ShardedDiscordClient _client { get; }
public class Ratelimiter public class Ratelimiter
{ {
@ -47,7 +45,8 @@ namespace NadekoBot.Modules.Administration
else else
{ {
usr.MessageCount++; usr.MessageCount++;
var t = Task.Run(async () => { var t = Task.Run(async () =>
{
try try
{ {
await Task.Delay(PerSeconds * 1000, cancelSource.Token); await Task.Delay(PerSeconds * 1000, cancelSource.Token);
@ -61,16 +60,17 @@ namespace NadekoBot.Modules.Administration
} }
} }
public RatelimitCommand() static RatelimitCommand()
{ {
this._client = NadekoBot.Client; _log = LogManager.GetCurrentClassLogger();
this._log = LogManager.GetCurrentClassLogger();
_client.MessageReceived += (umsg) => NadekoBot.Client.MessageReceived += async (umsg) =>
{ {
var t = Task.Run(async () => try
{ {
var usrMsg = umsg as IUserMessage; 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()) if (channel == null || usrMsg.IsAuthor())
@ -80,9 +80,9 @@ namespace NadekoBot.Modules.Administration
return; return;
if (limiter.CheckUserRatelimit(usrMsg.Author.Id)) if (limiter.CheckUserRatelimit(usrMsg.Author.Id))
try { await usrMsg.DeleteAsync(); } catch (Exception ex) { _log.Warn(ex); } await usrMsg.DeleteAsync();
}); }
return Task.CompletedTask; catch (Exception ex) { _log.Warn(ex); }
}; };
} }
@ -97,7 +97,7 @@ namespace NadekoBot.Modules.Administration
if (RatelimitingChannels.TryRemove(channel.Id, out throwaway)) if (RatelimitingChannels.TryRemove(channel.Id, out throwaway))
{ {
throwaway.cancelSource.Cancel(); throwaway.cancelSource.Cancel();
await channel.SendMessageAsync(" **Slow mode disabled.**").ConfigureAwait(false); await channel.SendConfirmAsync(" Slow mode disabled.").ConfigureAwait(false);
return; return;
} }
} }
@ -112,7 +112,7 @@ namespace NadekoBot.Modules.Administration
if (msg < 1 || perSec < 1 || msg > 100 || perSec > 3600) if (msg < 1 || perSec < 1 || msg > 100 || perSec > 3600)
{ {
await channel.SendMessageAsync("⚠️ `Invalid parameters.`"); await channel.SendErrorAsync("⚠️ Invalid parameters.");
return; return;
} }
var toAdd = new Ratelimiter() var toAdd = new Ratelimiter()
@ -123,8 +123,8 @@ namespace NadekoBot.Modules.Administration
}; };
if (RatelimitingChannels.TryAdd(channel.Id, toAdd)) if (RatelimitingChannels.TryAdd(channel.Id, toAdd))
{ {
await channel.SendMessageAsync("✅ **Slow mode initiated: " + await channel.SendConfirmAsync("Slow mode initiated",
$"Users can't send more than `{toAdd.MaxMessages} message(s)` every `{toAdd.PerSeconds} second(s)`.**") $"Users can't send more than `{toAdd.MaxMessages} message(s)` every `{toAdd.PerSeconds} second(s)`.")
.ConfigureAwait(false); .ConfigureAwait(false);
} }
} }

View File

@ -1,6 +1,7 @@
using Discord; using Discord;
using Discord.Commands; using Discord.Commands;
using NadekoBot.Attributes; using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Services; using NadekoBot.Services;
using NadekoBot.Services.Database.Models; using NadekoBot.Services.Database.Models;
using System; using System;
@ -27,12 +28,12 @@ namespace NadekoBot.Modules.Administration
bool newval; bool newval;
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
var config = uow.GuildConfigs.For(channel.Guild.Id); var config = uow.GuildConfigs.For(channel.Guild.Id, set => set);
newval = config.AutoDeleteSelfAssignedRoleMessages = !config.AutoDeleteSelfAssignedRoleMessages; newval = config.AutoDeleteSelfAssignedRoleMessages = !config.AutoDeleteSelfAssignedRoleMessages;
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
} }
await channel.SendMessageAsync($" Automatic deleting of `iam` and `iamn` confirmations has been {(newval ? "**enabled**" : "**disabled**")}.") await channel.SendConfirmAsync($" Automatic deleting of `iam` and `iamn` confirmations has been {(newval ? "**enabled**" : "**disabled**")}.")
.ConfigureAwait(false); .ConfigureAwait(false);
} }
@ -51,7 +52,8 @@ namespace NadekoBot.Modules.Administration
roles = uow.SelfAssignedRoles.GetFromGuild(channel.Guild.Id); roles = uow.SelfAssignedRoles.GetFromGuild(channel.Guild.Id);
if (roles.Any(s => s.RoleId == role.Id && s.GuildId == role.GuildId)) if (roles.Any(s => s.RoleId == role.Id && s.GuildId == role.GuildId))
{ {
msg = $"💢 Role **{role.Name}** is already in the list."; await channel.SendMessageAsync($"💢 Role **{role.Name}** is already in the list.").ConfigureAwait(false);
return;
} }
else else
{ {
@ -63,7 +65,7 @@ namespace NadekoBot.Modules.Administration
msg = $"🆗 Role **{role.Name}** added to the list."; msg = $"🆗 Role **{role.Name}** added to the list.";
} }
} }
await channel.SendMessageAsync(msg.ToString()).ConfigureAwait(false); await channel.SendConfirmAsync(msg.ToString()).ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -81,10 +83,10 @@ namespace NadekoBot.Modules.Administration
} }
if (!success) if (!success)
{ {
await channel.SendMessageAsync("❎ That role is not self-assignable.").ConfigureAwait(false); await channel.SendErrorAsync("❎ That role is not self-assignable.").ConfigureAwait(false);
return; return;
} }
await channel.SendMessageAsync($"🗑 **{role.Name}** has been removed from the list of self-assignable roles.").ConfigureAwait(false); await channel.SendConfirmAsync($"🗑 **{role.Name}** has been removed from the list of self-assignable roles.").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -96,10 +98,12 @@ namespace NadekoBot.Modules.Administration
var toRemove = new ConcurrentHashSet<SelfAssignedRole>(); var toRemove = new ConcurrentHashSet<SelfAssignedRole>();
var removeMsg = new StringBuilder(); var removeMsg = new StringBuilder();
var msg = new StringBuilder(); var msg = new StringBuilder();
var roleCnt = 0;
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
var roleModels = uow.SelfAssignedRoles.GetFromGuild(channel.Guild.Id); var roleModels = uow.SelfAssignedRoles.GetFromGuild(channel.Guild.Id).ToList();
msg.AppendLine($" There are `{roleModels.Count()}` self assignable roles:"); roleCnt = roleModels.Count;
msg.AppendLine();
foreach (var roleModel in roleModels) foreach (var roleModel in roleModels)
{ {
@ -119,7 +123,7 @@ namespace NadekoBot.Modules.Administration
} }
await uow.CompleteAsync(); await uow.CompleteAsync();
} }
await channel.SendMessageAsync(msg.ToString() + "\n\n" + removeMsg.ToString()).ConfigureAwait(false); await channel.SendConfirmAsync($" There are `{roleCnt}` self assignable roles:", msg.ToString() + "\n\n" + removeMsg.ToString()).ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -132,13 +136,13 @@ namespace NadekoBot.Modules.Administration
bool areExclusive; bool areExclusive;
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
var config = uow.GuildConfigs.For(channel.Guild.Id); var config = uow.GuildConfigs.For(channel.Guild.Id, set => set);
areExclusive = config.ExclusiveSelfAssignedRoles = !config.ExclusiveSelfAssignedRoles; areExclusive = config.ExclusiveSelfAssignedRoles = !config.ExclusiveSelfAssignedRoles;
await uow.CompleteAsync(); await uow.CompleteAsync();
} }
string exl = areExclusive ? "**exclusive**." : "**not exclusive**."; string exl = areExclusive ? "**exclusive**." : "**not exclusive**.";
await channel.SendMessageAsync(" Self assigned roles are now " + exl); await channel.SendConfirmAsync(" Self assigned roles are now " + exl);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -153,18 +157,18 @@ namespace NadekoBot.Modules.Administration
IEnumerable<SelfAssignedRole> roles; IEnumerable<SelfAssignedRole> roles;
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
conf = uow.GuildConfigs.For(channel.Guild.Id); conf = uow.GuildConfigs.For(channel.Guild.Id, set => set);
roles = uow.SelfAssignedRoles.GetFromGuild(channel.Guild.Id); roles = uow.SelfAssignedRoles.GetFromGuild(channel.Guild.Id);
} }
SelfAssignedRole roleModel; SelfAssignedRole roleModel;
if ((roleModel = roles.FirstOrDefault(r=>r.RoleId == role.Id)) == null) if ((roleModel = roles.FirstOrDefault(r=>r.RoleId == role.Id)) == null)
{ {
await channel.SendMessageAsync("💢 That role is not self-assignable.").ConfigureAwait(false); await channel.SendErrorAsync("That role is not self-assignable.").ConfigureAwait(false);
return; return;
} }
if (guildUser.Roles.Contains(role)) if (guildUser.Roles.Contains(role))
{ {
await channel.SendMessageAsync($"❎ You already have **{role.Name}** role.").ConfigureAwait(false); await channel.SendErrorAsync($"You already have **{role.Name}** role.").ConfigureAwait(false);
return; return;
} }
@ -173,7 +177,7 @@ namespace NadekoBot.Modules.Administration
var sameRoles = guildUser.Roles.Where(r => roles.Any(rm => rm.RoleId == r.Id)); var sameRoles = guildUser.Roles.Where(r => roles.Any(rm => rm.RoleId == r.Id));
if (sameRoles.Any()) if (sameRoles.Any())
{ {
await channel.SendMessageAsync($"❎ You already have **{sameRoles.FirstOrDefault().Name}** `exclusive self-assigned` role.").ConfigureAwait(false); await channel.SendErrorAsync($"You already have **{sameRoles.FirstOrDefault().Name}** `exclusive self-assigned` role.").ConfigureAwait(false);
return; return;
} }
} }
@ -183,20 +187,16 @@ namespace NadekoBot.Modules.Administration
} }
catch (Exception ex) catch (Exception ex)
{ {
await channel.SendMessageAsync($"⚠️ 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 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);
Console.WriteLine(ex); Console.WriteLine(ex);
return; return;
} }
var msg = await channel.SendMessageAsync($"🆗 You now have **{role.Name}** role.").ConfigureAwait(false); var msg = await channel.SendConfirmAsync($"🆗 You now have **{role.Name}** role.").ConfigureAwait(false);
if (conf.AutoDeleteSelfAssignedRoleMessages) if (conf.AutoDeleteSelfAssignedRoleMessages)
{ {
var t = Task.Run(async () => msg.DeleteAfter(3);
{ umsg.DeleteAfter(3);
await Task.Delay(3000).ConfigureAwait(false);
try { await msg.DeleteAsync().ConfigureAwait(false); } catch { } // if 502 or something, i don't want bot crashing
try { await usrMsg.DeleteAsync().ConfigureAwait(false); } catch { }
});
} }
} }
@ -207,22 +207,22 @@ namespace NadekoBot.Modules.Administration
var channel = (ITextChannel)umsg.Channel; var channel = (ITextChannel)umsg.Channel;
var guildUser = (IGuildUser)umsg.Author; var guildUser = (IGuildUser)umsg.Author;
GuildConfig conf; bool autoDeleteSelfAssignedRoleMessages;
IEnumerable<SelfAssignedRole> roles; IEnumerable<SelfAssignedRole> roles;
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
conf = uow.GuildConfigs.For(channel.Guild.Id); autoDeleteSelfAssignedRoleMessages = uow.GuildConfigs.For(channel.Guild.Id, set => set).AutoDeleteSelfAssignedRoleMessages;
roles = uow.SelfAssignedRoles.GetFromGuild(channel.Guild.Id); roles = uow.SelfAssignedRoles.GetFromGuild(channel.Guild.Id);
} }
SelfAssignedRole roleModel; SelfAssignedRole roleModel;
if ((roleModel = roles.FirstOrDefault(r => r.RoleId == role.Id)) == null) if ((roleModel = roles.FirstOrDefault(r => r.RoleId == role.Id)) == null)
{ {
await channel.SendMessageAsync("💢 That role is not self-assignable.").ConfigureAwait(false); await channel.SendErrorAsync("💢 That role is not self-assignable.").ConfigureAwait(false);
return; return;
} }
if (!guildUser.Roles.Contains(role)) if (!guildUser.Roles.Contains(role))
{ {
await channel.SendMessageAsync($"❎ You don't have **{role.Name}** role.").ConfigureAwait(false); await channel.SendErrorAsync($"❎ You don't have **{role.Name}** role.").ConfigureAwait(false);
return; return;
} }
try try
@ -231,19 +231,15 @@ namespace NadekoBot.Modules.Administration
} }
catch (Exception) catch (Exception)
{ {
await channel.SendMessageAsync($"⚠️ 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 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);
return; return;
} }
var msg = await channel.SendMessageAsync($"🆗 You no longer have **{role.Name}** role.").ConfigureAwait(false); var msg = await channel.SendConfirmAsync($"🆗 You no longer have **{role.Name}** role.").ConfigureAwait(false);
if (conf.AutoDeleteSelfAssignedRoleMessages) if (autoDeleteSelfAssignedRoleMessages)
{ {
var t = Task.Run(async () => msg.DeleteAfter(3);
{ umsg.DeleteAfter(3);
await Task.Delay(3000).ConfigureAwait(false);
try { await msg.DeleteAsync().ConfigureAwait(false); } catch { } // if 502 or something, i don't want bot crashing
try { await umsg.DeleteAsync().ConfigureAwait(false); } catch { }
});
} }
} }
} }

View File

@ -1,6 +1,7 @@
using Discord; using Discord;
using Discord.Commands; using Discord.Commands;
using NadekoBot.Attributes; using NadekoBot.Attributes;
using NadekoBot.Extensions;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -13,9 +14,9 @@ namespace NadekoBot.Modules.Administration
{ {
private ShardedDiscordClient _client; private ShardedDiscordClient _client;
public SelfCommands(ShardedDiscordClient client) public SelfCommands()
{ {
this._client = client; this._client = NadekoBot.Client;
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -31,18 +32,18 @@ namespace NadekoBot.Modules.Administration
if (server == null) if (server == null)
{ {
await channel.SendMessageAsync("⚠️ Cannot find that server").ConfigureAwait(false); await channel.SendErrorAsync("⚠️ Cannot find that server").ConfigureAwait(false);
return; return;
} }
if (server.OwnerId != _client.GetCurrentUser().Id) if (server.OwnerId != _client.GetCurrentUser().Id)
{ {
await server.LeaveAsync().ConfigureAwait(false); await server.LeaveAsync().ConfigureAwait(false);
await channel.SendMessageAsync("✅ Left server " + server.Name).ConfigureAwait(false); await channel.SendConfirmAsync("✅ Left server " + server.Name).ConfigureAwait(false);
} }
else else
{ {
await server.DeleteAsync().ConfigureAwait(false); await server.DeleteAsync().ConfigureAwait(false);
await channel.SendMessageAsync("Deleted server " + server.Name).ConfigureAwait(false); await channel.SendConfirmAsync("Deleted server " + server.Name).ConfigureAwait(false);
} }
} }
} }

View File

@ -17,26 +17,23 @@ namespace NadekoBot.Modules.Administration
[Group] [Group]
public class ServerGreetCommands public class ServerGreetCommands
{ {
public static long Greeted = 0; private static Logger _log { get; }
private Logger _log;
public ServerGreetCommands() static ServerGreetCommands()
{ {
NadekoBot.Client.UserJoined += UserJoined; NadekoBot.Client.UserJoined += UserJoined;
NadekoBot.Client.UserLeft += UserLeft; NadekoBot.Client.UserLeft += UserLeft;
_log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
} }
//todo optimize ASAP
private Task UserLeft(IGuildUser user) private static async void UserLeft(IGuildUser user)
{
var leftTask = Task.Run(async () =>
{ {
try try
{ {
GuildConfig conf; GuildConfig conf;
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
conf = uow.GuildConfigs.For(user.Guild.Id); conf = uow.GuildConfigs.For(user.Guild.Id, set => set);
} }
if (!conf.SendChannelByeMessage) return; if (!conf.SendChannelByeMessage) return;
@ -53,30 +50,22 @@ namespace NadekoBot.Modules.Administration
var toDelete = await channel.SendMessageAsync(msg.SanitizeMentions()).ConfigureAwait(false); var toDelete = await channel.SendMessageAsync(msg.SanitizeMentions()).ConfigureAwait(false);
if (conf.AutoDeleteByeMessagesTimer > 0) if (conf.AutoDeleteByeMessagesTimer > 0)
{ {
var t = Task.Run(async () => toDelete.DeleteAfter(conf.AutoDeleteByeMessagesTimer);
{
await Task.Delay(conf.AutoDeleteByeMessagesTimer * 1000).ConfigureAwait(false); // 5 minutes
try { await toDelete.DeleteAsync().ConfigureAwait(false); } catch { }
});
} }
} }
catch (Exception ex) { _log.Warn(ex); } catch (Exception ex) { _log.Warn(ex); }
} }
catch { } catch { }
});
return Task.CompletedTask;
} }
private Task UserJoined(IGuildUser user) private static async void UserJoined(IGuildUser user)
{
var joinedTask = Task.Run(async () =>
{ {
try try
{ {
GuildConfig conf; GuildConfig conf;
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
conf = uow.GuildConfigs.For(user.Guild.Id); conf = uow.GuildConfigs.For(user.Guild.Id, set => set);
} }
if (conf.SendChannelGreetMessage) if (conf.SendChannelGreetMessage)
@ -92,11 +81,7 @@ namespace NadekoBot.Modules.Administration
var toDelete = await channel.SendMessageAsync(msg.SanitizeMentions()).ConfigureAwait(false); var toDelete = await channel.SendMessageAsync(msg.SanitizeMentions()).ConfigureAwait(false);
if (conf.AutoDeleteGreetMessagesTimer > 0) if (conf.AutoDeleteGreetMessagesTimer > 0)
{ {
var t = Task.Run(async () => toDelete.DeleteAfter(conf.AutoDeleteGreetMessagesTimer);
{
await Task.Delay(conf.AutoDeleteGreetMessagesTimer * 1000).ConfigureAwait(false); // 5 minutes
try { await toDelete.DeleteAsync().ConfigureAwait(false); } catch { }
});
} }
} }
catch (Exception ex) { _log.Warn(ex); } catch (Exception ex) { _log.Warn(ex); }
@ -113,14 +98,12 @@ namespace NadekoBot.Modules.Administration
var msg = conf.DmGreetMessageText.Replace("%user%", user.Username).Replace("%server%", user.Guild.Name); var msg = conf.DmGreetMessageText.Replace("%user%", user.Username).Replace("%server%", user.Guild.Name);
if (!string.IsNullOrWhiteSpace(msg)) if (!string.IsNullOrWhiteSpace(msg))
{ {
await channel.SendMessageAsync(msg).ConfigureAwait(false); await channel.SendConfirmAsync(msg).ConfigureAwait(false);
} }
} }
} }
} }
catch { } catch { }
});
return Task.CompletedTask;
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -135,9 +118,9 @@ namespace NadekoBot.Modules.Administration
await ServerGreetCommands.SetGreetDel(channel.Guild.Id, timer).ConfigureAwait(false); await ServerGreetCommands.SetGreetDel(channel.Guild.Id, timer).ConfigureAwait(false);
if (timer > 0) if (timer > 0)
await channel.SendMessageAsync($"🆗 Greet messages **will be deleted** after `{timer} seconds`.").ConfigureAwait(false); await channel.SendConfirmAsync($"🆗 Greet messages **will be deleted** after `{timer} seconds`.").ConfigureAwait(false);
else else
await channel.SendMessageAsync(" Automatic deletion of greet messages has been **disabled**.").ConfigureAwait(false); await channel.SendConfirmAsync(" Automatic deletion of greet messages has been **disabled**.").ConfigureAwait(false);
} }
private static async Task SetGreetDel(ulong id, int timer) private static async Task SetGreetDel(ulong id, int timer)
@ -147,9 +130,9 @@ namespace NadekoBot.Modules.Administration
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
var conf = uow.GuildConfigs.For(id); var conf = uow.GuildConfigs.For(id, set => set);
conf.AutoDeleteGreetMessagesTimer = timer; conf.AutoDeleteGreetMessagesTimer = timer;
uow.GuildConfigs.Update(conf);
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
} }
} }
@ -164,9 +147,9 @@ namespace NadekoBot.Modules.Administration
var enabled = await ServerGreetCommands.SetGreet(channel.Guild.Id, channel.Id).ConfigureAwait(false); var enabled = await ServerGreetCommands.SetGreet(channel.Guild.Id, channel.Id).ConfigureAwait(false);
if (enabled) if (enabled)
await channel.SendMessageAsync("✅ Greeting messages **enabled** on this channel.").ConfigureAwait(false); await channel.SendConfirmAsync("✅ Greeting messages **enabled** on this channel.").ConfigureAwait(false);
else else
await channel.SendMessageAsync(" Greeting messages **disabled**.").ConfigureAwait(false); await channel.SendConfirmAsync(" Greeting messages **disabled**.").ConfigureAwait(false);
} }
private static async Task<bool> SetGreet(ulong guildId, ulong channelId, bool? value = null) private static async Task<bool> SetGreet(ulong guildId, ulong channelId, bool? value = null)
@ -174,10 +157,10 @@ namespace NadekoBot.Modules.Administration
bool enabled; bool enabled;
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
var conf = uow.GuildConfigs.For(guildId); var conf = uow.GuildConfigs.For(guildId, set => set);
enabled = conf.SendChannelGreetMessage = value ?? !conf.SendChannelGreetMessage; enabled = conf.SendChannelGreetMessage = value ?? !conf.SendChannelGreetMessage;
conf.GreetMessageChannelId = channelId; conf.GreetMessageChannelId = channelId;
uow.GuildConfigs.Update(conf);
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
} }
return enabled; return enabled;
@ -192,20 +175,20 @@ namespace NadekoBot.Modules.Administration
if (string.IsNullOrWhiteSpace(text)) if (string.IsNullOrWhiteSpace(text))
{ {
GuildConfig config; string channelGreetMessageText;
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
config = uow.GuildConfigs.For(channel.Guild.Id); channelGreetMessageText = uow.GuildConfigs.For(channel.Guild.Id, set => set).ChannelGreetMessageText;
} }
await channel.SendMessageAsync(" Current **greet** message: `" + config.ChannelGreetMessageText?.SanitizeMentions() + "`"); await channel.SendConfirmAsync("Current greet message: ", channelGreetMessageText?.SanitizeMentions());
return; return;
} }
var sendGreetEnabled = ServerGreetCommands.SetGreetMessage(channel.Guild.Id, ref text); var sendGreetEnabled = ServerGreetCommands.SetGreetMessage(channel.Guild.Id, ref text);
await channel.SendMessageAsync("🆗 New greet message **set**.").ConfigureAwait(false); await channel.SendConfirmAsync("🆗 New greet message **set**.").ConfigureAwait(false);
if (!sendGreetEnabled) if (!sendGreetEnabled)
await channel.SendMessageAsync(" Enable greet messsages by typing `.greet`").ConfigureAwait(false); await channel.SendConfirmAsync(" Enable greet messsages by typing `.greet`").ConfigureAwait(false);
} }
public static bool SetGreetMessage(ulong guildId, ref string message) public static bool SetGreetMessage(ulong guildId, ref string message)
@ -218,11 +201,10 @@ namespace NadekoBot.Modules.Administration
bool greetMsgEnabled; bool greetMsgEnabled;
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
var conf = uow.GuildConfigs.For(guildId); var conf = uow.GuildConfigs.For(guildId, set => set);
conf.ChannelGreetMessageText = message; conf.ChannelGreetMessageText = message;
greetMsgEnabled = conf.SendChannelGreetMessage; greetMsgEnabled = conf.SendChannelGreetMessage;
uow.GuildConfigs.Update(conf);
uow.Complete(); uow.Complete();
} }
return greetMsgEnabled; return greetMsgEnabled;
@ -238,9 +220,9 @@ namespace NadekoBot.Modules.Administration
var enabled = await ServerGreetCommands.SetGreetDm(channel.Guild.Id).ConfigureAwait(false); var enabled = await ServerGreetCommands.SetGreetDm(channel.Guild.Id).ConfigureAwait(false);
if (enabled) if (enabled)
await channel.SendMessageAsync("🆗 DM Greet announcements **enabled**.").ConfigureAwait(false); await channel.SendConfirmAsync("🆗 DM Greet announcements **enabled**.").ConfigureAwait(false);
else else
await channel.SendMessageAsync(" Greet announcements **disabled**.").ConfigureAwait(false); await channel.SendConfirmAsync(" Greet announcements **disabled**.").ConfigureAwait(false);
} }
private static async Task<bool> SetGreetDm(ulong guildId, bool? value = null) private static async Task<bool> SetGreetDm(ulong guildId, bool? value = null)
@ -248,9 +230,9 @@ namespace NadekoBot.Modules.Administration
bool enabled; bool enabled;
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
var conf = uow.GuildConfigs.For(guildId); var conf = uow.GuildConfigs.For(guildId, set => set);
enabled = conf.SendDmGreetMessage = value ?? !conf.SendDmGreetMessage; enabled = conf.SendDmGreetMessage = value ?? !conf.SendDmGreetMessage;
uow.GuildConfigs.Update(conf);
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
} }
return enabled; return enabled;
@ -270,15 +252,15 @@ namespace NadekoBot.Modules.Administration
{ {
config = uow.GuildConfigs.For(channel.Guild.Id); config = uow.GuildConfigs.For(channel.Guild.Id);
} }
await channel.SendMessageAsync(" Current **DM greet** message: `" + config.DmGreetMessageText?.SanitizeMentions() + "`"); await channel.SendConfirmAsync(" Current **DM greet** message: `" + config.DmGreetMessageText?.SanitizeMentions() + "`");
return; return;
} }
var sendGreetEnabled = ServerGreetCommands.SetGreetDmMessage(channel.Guild.Id, ref text); var sendGreetEnabled = ServerGreetCommands.SetGreetDmMessage(channel.Guild.Id, ref text);
await channel.SendMessageAsync("🆗 New DM greet message **set**.").ConfigureAwait(false); await channel.SendConfirmAsync("🆗 New DM greet message **set**.").ConfigureAwait(false);
if (!sendGreetEnabled) if (!sendGreetEnabled)
await channel.SendMessageAsync($" Enable DM greet messsages by typing `{NadekoBot.ModulePrefixes[typeof(Administration).Name]}greetdm`").ConfigureAwait(false); await channel.SendConfirmAsync($" Enable DM greet messsages by typing `{NadekoBot.ModulePrefixes[typeof(Administration).Name]}greetdm`").ConfigureAwait(false);
} }
public static bool SetGreetDmMessage(ulong guildId, ref string message) public static bool SetGreetDmMessage(ulong guildId, ref string message)
@ -295,7 +277,6 @@ namespace NadekoBot.Modules.Administration
conf.DmGreetMessageText = message; conf.DmGreetMessageText = message;
greetMsgEnabled = conf.SendDmGreetMessage; greetMsgEnabled = conf.SendDmGreetMessage;
uow.GuildConfigs.Update(conf);
uow.Complete(); uow.Complete();
} }
return greetMsgEnabled; return greetMsgEnabled;
@ -311,9 +292,9 @@ namespace NadekoBot.Modules.Administration
var enabled = await ServerGreetCommands.SetBye(channel.Guild.Id, channel.Id).ConfigureAwait(false); var enabled = await ServerGreetCommands.SetBye(channel.Guild.Id, channel.Id).ConfigureAwait(false);
if (enabled) if (enabled)
await channel.SendMessageAsync("✅ Bye announcements **enabled** on this channel.").ConfigureAwait(false); await channel.SendConfirmAsync("✅ Bye announcements **enabled** on this channel.").ConfigureAwait(false);
else else
await channel.SendMessageAsync(" Bye announcements **disabled**.").ConfigureAwait(false); await channel.SendConfirmAsync(" Bye announcements **disabled**.").ConfigureAwait(false);
} }
private static async Task<bool> SetBye(ulong guildId, ulong channelId, bool? value = null) private static async Task<bool> SetBye(ulong guildId, ulong channelId, bool? value = null)
@ -321,10 +302,10 @@ namespace NadekoBot.Modules.Administration
bool enabled; bool enabled;
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
var conf = uow.GuildConfigs.For(guildId); var conf = uow.GuildConfigs.For(guildId, set => set);
enabled = conf.SendChannelByeMessage = value ?? !conf.SendChannelByeMessage; enabled = conf.SendChannelByeMessage = value ?? !conf.SendChannelByeMessage;
conf.ByeMessageChannelId = channelId; conf.ByeMessageChannelId = channelId;
uow.GuildConfigs.Update(conf);
await uow.CompleteAsync(); await uow.CompleteAsync();
} }
return enabled; return enabled;
@ -339,20 +320,20 @@ namespace NadekoBot.Modules.Administration
if (string.IsNullOrWhiteSpace(text)) if (string.IsNullOrWhiteSpace(text))
{ {
GuildConfig config; string byeMessageText;
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
config = uow.GuildConfigs.For(channel.Guild.Id); byeMessageText = uow.GuildConfigs.For(channel.Guild.Id, set => set).ChannelByeMessageText;
} }
await channel.SendMessageAsync(" Current **bye** message: `" + config.ChannelByeMessageText?.SanitizeMentions() + "`"); await channel.SendConfirmAsync(" Current **bye** message: `" + byeMessageText?.SanitizeMentions() + "`");
return; return;
} }
var sendByeEnabled = ServerGreetCommands.SetByeMessage(channel.Guild.Id, ref text); var sendByeEnabled = ServerGreetCommands.SetByeMessage(channel.Guild.Id, ref text);
await channel.SendMessageAsync("🆗 New bye message **set**.").ConfigureAwait(false); await channel.SendConfirmAsync("🆗 New bye message **set**.").ConfigureAwait(false);
if (!sendByeEnabled) if (!sendByeEnabled)
await channel.SendMessageAsync($" Enable bye messsages by typing `{NadekoBot.ModulePrefixes[typeof(Administration).Name]}bye`").ConfigureAwait(false); await channel.SendConfirmAsync($" Enable bye messsages by typing `{NadekoBot.ModulePrefixes[typeof(Administration).Name]}bye`").ConfigureAwait(false);
} }
public static bool SetByeMessage(ulong guildId, ref string message) public static bool SetByeMessage(ulong guildId, ref string message)
@ -365,11 +346,10 @@ namespace NadekoBot.Modules.Administration
bool byeMsgEnabled; bool byeMsgEnabled;
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
var conf = uow.GuildConfigs.For(guildId); var conf = uow.GuildConfigs.For(guildId, set => set);
conf.ChannelByeMessageText = message; conf.ChannelByeMessageText = message;
byeMsgEnabled = conf.SendChannelByeMessage; byeMsgEnabled = conf.SendChannelByeMessage;
uow.GuildConfigs.Update(conf);
uow.Complete(); uow.Complete();
} }
return byeMsgEnabled; return byeMsgEnabled;
@ -385,9 +365,9 @@ namespace NadekoBot.Modules.Administration
await ServerGreetCommands.SetByeDel(channel.Guild.Id, timer).ConfigureAwait(false); await ServerGreetCommands.SetByeDel(channel.Guild.Id, timer).ConfigureAwait(false);
if (timer > 0) if (timer > 0)
await channel.SendMessageAsync($"🆗 Bye messages **will be deleted** after `{timer} seconds`.").ConfigureAwait(false); await channel.SendConfirmAsync($"🆗 Bye messages **will be deleted** after `{timer} seconds`.").ConfigureAwait(false);
else else
await channel.SendMessageAsync(" Automatic deletion of bye messages has been **disabled**.").ConfigureAwait(false); await channel.SendConfirmAsync(" Automatic deletion of bye messages has been **disabled**.").ConfigureAwait(false);
} }
private static async Task SetByeDel(ulong id, int timer) private static async Task SetByeDel(ulong id, int timer)
@ -397,9 +377,9 @@ namespace NadekoBot.Modules.Administration
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
var conf = uow.GuildConfigs.For(id); var conf = uow.GuildConfigs.For(id, set => set);
conf.AutoDeleteByeMessagesTimer = timer; conf.AutoDeleteByeMessagesTimer = timer;
uow.GuildConfigs.Update(conf);
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
} }
} }

View File

@ -17,10 +17,10 @@ namespace NadekoBot.Modules.Administration
[Group] [Group]
public class VoicePlusTextCommands public class VoicePlusTextCommands
{ {
Regex channelNameRegex = new Regex(@"[^a-zA-Z0-9 -]", RegexOptions.Compiled); private static Regex channelNameRegex = new Regex(@"[^a-zA-Z0-9 -]", RegexOptions.Compiled);
private ConcurrentHashSet<ulong> voicePlusTextCache; private static ConcurrentHashSet<ulong> voicePlusTextCache { get; }
public VoicePlusTextCommands() static VoicePlusTextCommands()
{ {
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
@ -29,15 +29,14 @@ namespace NadekoBot.Modules.Administration
NadekoBot.Client.UserVoiceStateUpdated += UserUpdatedEventHandler; NadekoBot.Client.UserVoiceStateUpdated += UserUpdatedEventHandler;
} }
private Task UserUpdatedEventHandler(IUser iuser, IVoiceState before, IVoiceState after) private static async void UserUpdatedEventHandler(IUser iuser, IVoiceState before, IVoiceState after)
{ {
var user = (iuser as IGuildUser); var user = (iuser as IGuildUser);
var guild = user?.Guild; var guild = user?.Guild;
if (guild == null) if (guild == null)
return Task.CompletedTask; return;
var task = Task.Run(async () =>
{
try try
{ {
var botUserPerms = guild.GetCurrentUser().GuildPermissions; var botUserPerms = guild.GetCurrentUser().GuildPermissions;
@ -51,14 +50,14 @@ namespace NadekoBot.Modules.Administration
{ {
try try
{ {
await (await guild.GetOwnerAsync()).SendMessageAsync( await (await guild.GetOwnerAsync()).SendErrorAsync(
"⚠️ I don't have **manage server** and/or **manage channels** permission," + "⚠️ I don't have **manage server** and/or **manage channels** permission," +
$" so I cannot run `voice+text` on **{guild.Name}** server.").ConfigureAwait(false); $" so I cannot run `voice+text` on **{guild.Name}** server.").ConfigureAwait(false);
} }
catch { } catch { }
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
uow.GuildConfigs.For(guild.Id).VoicePlusTextEnabled = false; uow.GuildConfigs.For(guild.Id, set => set).VoicePlusTextEnabled = false;
voicePlusTextCache.TryRemove(guild.Id); voicePlusTextCache.TryRemove(guild.Id);
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
} }
@ -97,11 +96,9 @@ namespace NadekoBot.Modules.Administration
{ {
Console.WriteLine(ex); Console.WriteLine(ex);
} }
});
return Task.CompletedTask;
} }
private string GetChannelName(string voiceName) => private static string GetChannelName(string voiceName) =>
channelNameRegex.Replace(voiceName, "").Trim().Replace(" ", "-").TrimTo(90, true) + "-voice"; channelNameRegex.Replace(voiceName, "").Trim().Replace(" ", "-").TrimTo(90, true) + "-voice";
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -116,7 +113,7 @@ namespace NadekoBot.Modules.Administration
var botUser = await guild.GetCurrentUserAsync().ConfigureAwait(false); var botUser = await guild.GetCurrentUserAsync().ConfigureAwait(false);
if (!botUser.GuildPermissions.ManageRoles || !botUser.GuildPermissions.ManageChannels) if (!botUser.GuildPermissions.ManageRoles || !botUser.GuildPermissions.ManageChannels)
{ {
await channel.SendMessageAsync("💢 I require atleast **manage roles** and **manage channels permissions** to enable this feature. `(preffered Administration permission)`"); await channel.SendErrorAsync("I require atleast **manage roles** and **manage channels permissions** to enable this feature. `(preffered Administration permission)`");
return; return;
} }
@ -124,7 +121,7 @@ namespace NadekoBot.Modules.Administration
{ {
try try
{ {
await channel.SendMessageAsync("⚠️ You are enabling this feature and **I do not have ADMINISTRATOR permissions**. " + await 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.`"); "`This may cause some issues, and you will have to clean up text channels yourself afterwards.`");
} }
catch { } catch { }
@ -134,7 +131,7 @@ namespace NadekoBot.Modules.Administration
bool isEnabled; bool isEnabled;
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
var conf = uow.GuildConfigs.For(guild.Id); var conf = uow.GuildConfigs.For(guild.Id, set => set);
isEnabled = conf.VoicePlusTextEnabled = !conf.VoicePlusTextEnabled; isEnabled = conf.VoicePlusTextEnabled = !conf.VoicePlusTextEnabled;
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
} }
@ -145,16 +142,16 @@ namespace NadekoBot.Modules.Administration
{ {
try { await textChannel.DeleteAsync().ConfigureAwait(false); } catch { } try { await textChannel.DeleteAsync().ConfigureAwait(false); } catch { }
} }
await channel.SendMessageAsync(" Successfuly **removed** voice + text feature.").ConfigureAwait(false); await channel.SendConfirmAsync(" Successfuly **removed** voice + text feature.").ConfigureAwait(false);
return; return;
} }
voicePlusTextCache.Add(guild.Id); voicePlusTextCache.Add(guild.Id);
await channel.SendMessageAsync("🆗 Successfuly **enabled** voice + text feature.").ConfigureAwait(false); await channel.SendConfirmAsync("🆗 Successfuly **enabled** voice + text feature.").ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {
await channel.SendMessageAsync(ex.ToString()).ConfigureAwait(false); await channel.SendErrorAsync(ex.ToString()).ConfigureAwait(false);
} }
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -168,7 +165,7 @@ namespace NadekoBot.Modules.Administration
var botUser = await guild.GetCurrentUserAsync().ConfigureAwait(false); var botUser = await guild.GetCurrentUserAsync().ConfigureAwait(false);
if (!botUser.GuildPermissions.Administrator) if (!botUser.GuildPermissions.Administrator)
{ {
await channel.SendMessageAsync("⚠️ I need **Administrator permission** to do that.").ConfigureAwait(false); await channel.SendErrorAsync("I need **Administrator permission** to do that.").ConfigureAwait(false);
return; return;
} }
@ -183,7 +180,7 @@ namespace NadekoBot.Modules.Administration
await Task.Delay(500); await Task.Delay(500);
} }
await channel.SendMessageAsync("✅ Done.").ConfigureAwait(false); await channel.SendConfirmAsync("Cleaned v+t.").ConfigureAwait(false);
} }
} }
} }

View File

@ -10,6 +10,8 @@ using NadekoBot.Attributes;
using Discord.WebSocket; using Discord.WebSocket;
using NadekoBot.Services.Database.Models; using NadekoBot.Services.Database.Models;
using System.Linq; using System.Linq;
using NadekoBot.Extensions;
using System.Threading;
namespace NadekoBot.Modules.ClashOfClans namespace NadekoBot.Modules.ClashOfClans
{ {
@ -18,6 +20,8 @@ namespace NadekoBot.Modules.ClashOfClans
{ {
public static ConcurrentDictionary<ulong, List<ClashWar>> ClashWars { get; set; } = new ConcurrentDictionary<ulong, List<ClashWar>>(); public static ConcurrentDictionary<ulong, List<ClashWar>> ClashWars { get; set; } = new ConcurrentDictionary<ulong, List<ClashWar>>();
private static Timer checkWarTimer { get; }
static ClashOfClans() static ClashOfClans()
{ {
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
@ -31,13 +35,21 @@ namespace NadekoBot.Modules.ClashOfClans
?.GetTextChannel(cw.ChannelId); ?.GetTextChannel(cw.ChannelId);
return cw; return cw;
}) })
.Where(cw => cw?.Channel != null) .Where(cw => cw.Channel != null)
.GroupBy(cw => cw.GuildId) .GroupBy(cw => cw.GuildId)
.ToDictionary(g => g.Key, g => g.ToList())); .ToDictionary(g => g.Key, g => g.ToList()));
} }
}
public ClashOfClans(ILocalization loc, CommandService cmds, ShardedDiscordClient client) : base(loc, cmds, client) checkWarTimer = new Timer(async _ =>
{ {
foreach (var kvp in ClashWars)
{
foreach (var war in kvp.Value)
{
try { await CheckWar(TimeSpan.FromHours(2), war).ConfigureAwait(false); } catch { }
}
}
}, null, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1));
} }
private static async Task CheckWar(TimeSpan callExpire, ClashWar war) private static async Task CheckWar(TimeSpan callExpire, ClashWar war)
@ -45,11 +57,20 @@ namespace NadekoBot.Modules.ClashOfClans
var Bases = war.Bases; var Bases = war.Bases;
for (var i = 0; i < Bases.Count; i++) for (var i = 0; i < Bases.Count; i++)
{ {
if (Bases[i].CallUser == null) continue; var callUser = Bases[i].CallUser;
if (!Bases[i].BaseDestroyed && DateTime.UtcNow - Bases[i].TimeAdded >= callExpire) if (callUser == null) continue;
if ((!Bases[i].BaseDestroyed) && DateTime.UtcNow - Bases[i].TimeAdded >= callExpire)
{ {
if (Bases[i].Stars != 3)
Bases[i].BaseDestroyed = true;
else
Bases[i] = null; Bases[i] = null;
try { await war.Channel.SendMessageAsync($"❗🔰**Claim from @{Bases[i].CallUser} for a war against {war.ShortPrint()} has expired.**").ConfigureAwait(false); } catch { } try
{
SaveWar(war);
await war.Channel.SendErrorAsync($"❗🔰**Claim from @{Bases[i].CallUser} for a war against {war.ShortPrint()} has expired.**").ConfigureAwait(false);
}
catch { }
} }
} }
} }
@ -68,7 +89,7 @@ namespace NadekoBot.Modules.ClashOfClans
if (size < 10 || size > 50 || size % 5 != 0) if (size < 10 || size > 50 || size % 5 != 0)
{ {
await channel.SendMessageAsync("💢🔰 Not a Valid war size").ConfigureAwait(false); await channel.SendErrorAsync("🔰 Not a Valid war size").ConfigureAwait(false);
return; return;
} }
List<ClashWar> wars; List<ClashWar> wars;
@ -83,7 +104,7 @@ namespace NadekoBot.Modules.ClashOfClans
var cw = await CreateWar(enemyClan, size, channel.Guild.Id, umsg.Channel.Id); var cw = await CreateWar(enemyClan, size, channel.Guild.Id, umsg.Channel.Id);
wars.Add(cw); wars.Add(cw);
await channel.SendMessageAsync($"❗🔰**CREATED CLAN WAR AGAINST {cw.ShortPrint()}**").ConfigureAwait(false); await channel.SendConfirmAsync($"❗🔰**CREATED CLAN WAR AGAINST {cw.ShortPrint()}**").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -98,18 +119,18 @@ namespace NadekoBot.Modules.ClashOfClans
var warsInfo = GetWarInfo(umsg, num); var warsInfo = GetWarInfo(umsg, num);
if (warsInfo == null) if (warsInfo == null)
{ {
await channel.SendMessageAsync("💢🔰 **That war does not exist.**").ConfigureAwait(false); await channel.SendErrorAsync("🔰 **That war does not exist.**").ConfigureAwait(false);
return; return;
} }
var war = warsInfo.Item1[warsInfo.Item2]; var war = warsInfo.Item1[warsInfo.Item2];
try try
{ {
war.Start(); war.Start();
await channel.SendMessageAsync($"🔰**STARTED WAR AGAINST {war.ShortPrint()}**").ConfigureAwait(false); await channel.SendConfirmAsync($"🔰**STARTED WAR AGAINST {war.ShortPrint()}**").ConfigureAwait(false);
} }
catch catch
{ {
await channel.SendMessageAsync($"🔰**WAR AGAINST {war.ShortPrint()} HAS ALREADY STARTED**").ConfigureAwait(false); await channel.SendErrorAsync($"🔰**WAR AGAINST {war.ShortPrint()} HAS ALREADY STARTED**").ConfigureAwait(false);
} }
SaveWar(war); SaveWar(war);
} }
@ -128,7 +149,7 @@ namespace NadekoBot.Modules.ClashOfClans
ClashWars.TryGetValue(channel.Guild.Id, out wars); ClashWars.TryGetValue(channel.Guild.Id, out wars);
if (wars == null || wars.Count == 0) if (wars == null || wars.Count == 0)
{ {
await channel.SendMessageAsync("🔰 **No active wars.**").ConfigureAwait(false); await channel.SendErrorAsync("🔰 **No active wars.**").ConfigureAwait(false);
return; return;
} }
@ -141,7 +162,7 @@ namespace NadekoBot.Modules.ClashOfClans
sb.AppendLine($"\t\t`Size:` **{wars[i].Size} v {wars[i].Size}**"); sb.AppendLine($"\t\t`Size:` **{wars[i].Size} v {wars[i].Size}**");
sb.AppendLine("**-------------------------**"); sb.AppendLine("**-------------------------**");
} }
await channel.SendMessageAsync(sb.ToString()).ConfigureAwait(false); await channel.SendConfirmAsync(sb.ToString()).ConfigureAwait(false);
return; return;
} }
@ -151,10 +172,10 @@ namespace NadekoBot.Modules.ClashOfClans
var warsInfo = GetWarInfo(umsg, num); var warsInfo = GetWarInfo(umsg, num);
if (warsInfo == null) if (warsInfo == null)
{ {
await channel.SendMessageAsync("💢🔰 **That war does not exist.**").ConfigureAwait(false); await channel.SendErrorAsync("🔰 **That war does not exist.**").ConfigureAwait(false);
return; return;
} }
await channel.SendMessageAsync(warsInfo.Item1[warsInfo.Item2].ToPrettyString()).ConfigureAwait(false); await channel.SendConfirmAsync(warsInfo.Item1[warsInfo.Item2].ToPrettyString()).ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -165,7 +186,7 @@ namespace NadekoBot.Modules.ClashOfClans
var warsInfo = GetWarInfo(umsg, number); var warsInfo = GetWarInfo(umsg, number);
if (warsInfo == null || warsInfo.Item1.Count == 0) if (warsInfo == null || warsInfo.Item1.Count == 0)
{ {
await channel.SendMessageAsync("💢🔰 **That war does not exist.**").ConfigureAwait(false); await channel.SendErrorAsync("🔰 **That war does not exist.**").ConfigureAwait(false);
return; return;
} }
var usr = var usr =
@ -177,11 +198,11 @@ namespace NadekoBot.Modules.ClashOfClans
var war = warsInfo.Item1[warsInfo.Item2]; var war = warsInfo.Item1[warsInfo.Item2];
war.Call(usr, baseNumber - 1); war.Call(usr, baseNumber - 1);
SaveWar(war); SaveWar(war);
await channel.SendMessageAsync($"🔰**{usr}** claimed a base #{baseNumber} for a war against {war.ShortPrint()}").ConfigureAwait(false); await channel.SendConfirmAsync($"🔰**{usr}** claimed a base #{baseNumber} for a war against {war.ShortPrint()}").ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {
await channel.SendMessageAsync($"💢🔰 {ex.Message}").ConfigureAwait(false); await channel.SendErrorAsync($"🔰 {ex.Message}").ConfigureAwait(false);
} }
} }
@ -218,13 +239,13 @@ namespace NadekoBot.Modules.ClashOfClans
var warsInfo = GetWarInfo(umsg, number); var warsInfo = GetWarInfo(umsg, number);
if (warsInfo == null) if (warsInfo == null)
{ {
await channel.SendMessageAsync("💢🔰 That war does not exist.").ConfigureAwait(false); await channel.SendErrorAsync("🔰 That war does not exist.").ConfigureAwait(false);
return; return;
} }
var war = warsInfo.Item1[warsInfo.Item2]; var war = warsInfo.Item1[warsInfo.Item2];
war.End(); war.End();
SaveWar(war); SaveWar(war);
await channel.SendMessageAsync($"❗🔰**War against {warsInfo.Item1[warsInfo.Item2].ShortPrint()} ended.**").ConfigureAwait(false); await channel.SendConfirmAsync($"❗🔰**War against {warsInfo.Item1[warsInfo.Item2].ShortPrint()} ended.**").ConfigureAwait(false);
var size = warsInfo.Item1[warsInfo.Item2].Size; var size = warsInfo.Item1[warsInfo.Item2].Size;
warsInfo.Item1.RemoveAt(warsInfo.Item2); warsInfo.Item1.RemoveAt(warsInfo.Item2);
@ -239,7 +260,7 @@ namespace NadekoBot.Modules.ClashOfClans
var warsInfo = GetWarInfo(umsg, number); var warsInfo = GetWarInfo(umsg, number);
if (warsInfo == null || warsInfo.Item1.Count == 0) if (warsInfo == null || warsInfo.Item1.Count == 0)
{ {
await channel.SendMessageAsync("💢🔰 **That war does not exist.**").ConfigureAwait(false); await channel.SendErrorAsync("🔰 **That war does not exist.**").ConfigureAwait(false);
return; return;
} }
var usr = var usr =
@ -251,11 +272,11 @@ namespace NadekoBot.Modules.ClashOfClans
var war = warsInfo.Item1[warsInfo.Item2]; var war = warsInfo.Item1[warsInfo.Item2];
var baseNumber = war.Uncall(usr); var baseNumber = war.Uncall(usr);
SaveWar(war); SaveWar(war);
await channel.SendMessageAsync($"🔰 @{usr} has **UNCLAIMED** a base #{baseNumber + 1} from a war against {war.ShortPrint()}").ConfigureAwait(false); await channel.SendConfirmAsync($"🔰 @{usr} has **UNCLAIMED** a base #{baseNumber + 1} from a war against {war.ShortPrint()}").ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {
await channel.SendMessageAsync($"💢🔰 {ex.Message}").ConfigureAwait(false); await channel.SendErrorAsync($"🔰 {ex.Message}").ConfigureAwait(false);
} }
} }
@ -265,7 +286,7 @@ namespace NadekoBot.Modules.ClashOfClans
var warInfo = GetWarInfo(umsg, number); var warInfo = GetWarInfo(umsg, number);
if (warInfo == null || warInfo.Item1.Count == 0) if (warInfo == null || warInfo.Item1.Count == 0)
{ {
await channel.SendMessageAsync("💢🔰 **That war does not exist.**").ConfigureAwait(false); await channel.SendErrorAsync("🔰 **That war does not exist.**").ConfigureAwait(false);
return; return;
} }
var war = warInfo.Item1[warInfo.Item2]; var war = warInfo.Item1[warInfo.Item2];
@ -280,11 +301,11 @@ namespace NadekoBot.Modules.ClashOfClans
{ {
war.FinishClaim(baseNumber, stars); war.FinishClaim(baseNumber, stars);
} }
await channel.SendMessageAsync($"❗🔰{umsg.Author.Mention} **DESTROYED** a base #{baseNumber + 1} in a war against {war.ShortPrint()}").ConfigureAwait(false); await channel.SendConfirmAsync($"❗🔰{umsg.Author.Mention} **DESTROYED** a base #{baseNumber + 1} in a war against {war.ShortPrint()}").ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {
await channel.SendMessageAsync($"💢🔰 {ex.Message}").ConfigureAwait(false); await channel.SendErrorAsync($"🔰 {ex.Message}").ConfigureAwait(false);
} }
} }

View File

@ -28,8 +28,8 @@ namespace NadekoBot.Modules.ClashOfClans
{ {
if (baseNumber < 0 || baseNumber >= cw.Bases.Count) if (baseNumber < 0 || baseNumber >= cw.Bases.Count)
throw new ArgumentException("Invalid base number"); throw new ArgumentException("Invalid base number");
if (cw.Bases[baseNumber].CallUser != null) if (cw.Bases[baseNumber].CallUser != null && cw.Bases[baseNumber].Stars == 3)
throw new ArgumentException("That base is already claimed."); throw new ArgumentException("That base is already destroyed.");
for (var i = 0; i < cw.Bases.Count; i++) for (var i = 0; i < cw.Bases.Count; i++)
{ {
if (cw.Bases[i]?.BaseDestroyed == false && cw.Bases[i]?.CallUser == u) if (cw.Bases[i]?.BaseDestroyed == false && cw.Bases[i]?.CallUser == u)
@ -95,8 +95,15 @@ namespace NadekoBot.Modules.ClashOfClans
else else
{ {
var left = (cw.WarState == StateOfWar.Started) ? twoHours - (DateTime.UtcNow - cw.Bases[i].TimeAdded) : twoHours; var left = (cw.WarState == StateOfWar.Started) ? twoHours - (DateTime.UtcNow - cw.Bases[i].TimeAdded) : twoHours;
if (cw.Bases[i].Stars == 3)
{
sb.AppendLine($"`{i + 1}.` ✅ `{cw.Bases[i].CallUser}` {left.Hours}h {left.Minutes}m {left.Seconds}s left"); sb.AppendLine($"`{i + 1}.` ✅ `{cw.Bases[i].CallUser}` {left.Hours}h {left.Minutes}m {left.Seconds}s left");
} }
else
{
sb.AppendLine($"`{i + 1}.` ✅ `{cw.Bases[i].CallUser}` {left.Hours}h {left.Minutes}m {left.Seconds}s left {new string('⭐', cw.Bases[i].Stars)} {string.Concat(Enumerable.Repeat("🔸", 3 - cw.Bases[i].Stars))}");
}
}
} }
} }

View File

@ -16,6 +16,8 @@ namespace NadekoBot.Modules.CustomReactions
public static ConcurrentHashSet<CustomReaction> GlobalReactions { get; } = new ConcurrentHashSet<CustomReaction>(); public static ConcurrentHashSet<CustomReaction> GlobalReactions { get; } = new ConcurrentHashSet<CustomReaction>();
public static ConcurrentDictionary<ulong, ConcurrentHashSet<CustomReaction>> GuildReactions { get; } = new ConcurrentDictionary<ulong, ConcurrentHashSet<CustomReaction>>(); public static ConcurrentDictionary<ulong, ConcurrentHashSet<CustomReaction>> GuildReactions { get; } = new ConcurrentDictionary<ulong, ConcurrentHashSet<CustomReaction>>();
public static ConcurrentDictionary<string, uint> ReactionStats { get; } = new ConcurrentDictionary<string, uint>();
static CustomReactions() static CustomReactions()
{ {
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
@ -25,10 +27,12 @@ namespace NadekoBot.Modules.CustomReactions
GlobalReactions = new ConcurrentHashSet<CustomReaction>(items.Where(g => g.GuildId == null || g.GuildId == 0)); GlobalReactions = new ConcurrentHashSet<CustomReaction>(items.Where(g => g.GuildId == null || g.GuildId == 0));
} }
} }
public CustomReactions(ILocalization loc, CommandService cmds, ShardedDiscordClient client) : base(loc, cmds, client) public CustomReactions() : base()
{ {
} }
public void ClearStats() => ReactionStats.Clear();
public static async Task<bool> TryExecuteCustomReaction(IUserMessage umsg) public static async Task<bool> TryExecuteCustomReaction(IUserMessage umsg)
{ {
var channel = umsg.Channel as ITextChannel; var channel = umsg.Channel as ITextChannel;
@ -40,14 +44,18 @@ namespace NadekoBot.Modules.CustomReactions
GuildReactions.TryGetValue(channel.Guild.Id, out reactions); GuildReactions.TryGetValue(channel.Guild.Id, out reactions);
if (reactions != null && reactions.Any()) if (reactions != null && reactions.Any())
{ {
var reaction = reactions.Where(cr => { var reaction = reactions.Where(cr =>
{
var hasTarget = cr.Response.ToLowerInvariant().Contains("%target%"); var hasTarget = cr.Response.ToLowerInvariant().Contains("%target%");
var trigger = cr.TriggerWithContext(umsg).Trim().ToLowerInvariant(); var trigger = cr.TriggerWithContext(umsg).Trim().ToLowerInvariant();
return ((hasTarget && content.StartsWith(trigger + " ")) || content == trigger); return ((hasTarget && content.StartsWith(trigger + " ")) || content == trigger);
}).Shuffle().FirstOrDefault(); }).Shuffle().FirstOrDefault();
if (reaction != null) if (reaction != null)
{ {
if (reaction.Response != "-")
try { await channel.SendMessageAsync(reaction.ResponseWithContext(umsg)).ConfigureAwait(false); } catch { } try { await channel.SendMessageAsync(reaction.ResponseWithContext(umsg)).ConfigureAwait(false); } catch { }
ReactionStats.AddOrUpdate(reaction.Trigger, 1, (k, old) => ++old);
return true; return true;
} }
} }
@ -61,6 +69,7 @@ namespace NadekoBot.Modules.CustomReactions
if (greaction != null) if (greaction != null)
{ {
try { await channel.SendMessageAsync(greaction.ResponseWithContext(umsg)).ConfigureAwait(false); } catch { } try { await channel.SendMessageAsync(greaction.ResponseWithContext(umsg)).ConfigureAwait(false); } catch { }
ReactionStats.AddOrUpdate(greaction.Trigger, 1, (k, old) => ++old);
return true; return true;
} }
return false; return false;
@ -77,7 +86,7 @@ namespace NadekoBot.Modules.CustomReactions
if ((channel == null && !NadekoBot.Credentials.IsOwner(imsg.Author)) || (channel != null && !((IGuildUser)imsg.Author).GuildPermissions.Administrator)) if ((channel == null && !NadekoBot.Credentials.IsOwner(imsg.Author)) || (channel != null && !((IGuildUser)imsg.Author).GuildPermissions.Administrator))
{ {
try { await imsg.Channel.SendMessageAsync("Insufficient permissions. Requires Bot ownership for global custom reactions, and Administrator for guild custom reactions."); } catch { } try { await imsg.Channel.SendErrorAsync("Insufficient permissions. Requires Bot ownership for global custom reactions, and Administrator for guild custom reactions."); } catch { }
return; return;
} }
@ -106,7 +115,12 @@ namespace NadekoBot.Modules.CustomReactions
reactions.Add(cr); reactions.Add(cr);
} }
await imsg.Channel.SendMessageAsync($"`Added new custom reaction {cr.Id}:`\n\t`Trigger:` {key}\n\t`Response:` {message}").ConfigureAwait(false); await imsg.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
.WithTitle("New Custom Reaction")
.WithDescription($"#{cr.Id}")
.AddField(efb => efb.WithName("Trigger").WithValue(key))
.AddField(efb => efb.WithName("Response").WithValue(message))
.Build()).ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -124,12 +138,11 @@ namespace NadekoBot.Modules.CustomReactions
customReactions = GuildReactions.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet<CustomReaction>()); customReactions = GuildReactions.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet<CustomReaction>());
if (customReactions == null || !customReactions.Any()) if (customReactions == null || !customReactions.Any())
await imsg.Channel.SendMessageAsync("`No custom reactions found`").ConfigureAwait(false); await imsg.Channel.SendErrorAsync("No custom reactions found").ConfigureAwait(false);
else else
await imsg.Channel.SendMessageAsync( await imsg.Channel.SendConfirmAsync(
$"`Page {page} of custom reactions:`\n" + $"Page {page} of custom reactions:",
string.Join("\n", customReactions string.Join("\n", customReactions.OrderBy(cr => cr.Trigger)
.OrderBy(cr => cr.Trigger)
.Skip((page - 1) * 20) .Skip((page - 1) * 20)
.Take(20) .Take(20)
.Select(cr => $"`#{cr.Id}` `Trigger:` {cr.Trigger}"))) .Select(cr => $"`#{cr.Id}` `Trigger:` {cr.Trigger}")))
@ -154,12 +167,12 @@ namespace NadekoBot.Modules.CustomReactions
customReactions = GuildReactions.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet<CustomReaction>()); customReactions = GuildReactions.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet<CustomReaction>());
if (customReactions == null || !customReactions.Any()) if (customReactions == null || !customReactions.Any())
await imsg.Channel.SendMessageAsync("`No custom reactions found`").ConfigureAwait(false); await imsg.Channel.SendErrorAsync("No custom reactions found").ConfigureAwait(false);
else else
{ {
var txtStream = await customReactions.GroupBy(cr => cr.Trigger) var txtStream = await customReactions.GroupBy(cr => cr.Trigger)
.OrderBy(cr => cr.Key) .OrderBy(cr => cr.Key)
.Select(cr => new { Trigger = cr.Key, Responses = cr.Select(y => y.Response).ToList() }) .Select(cr => new { Trigger = cr.Key, Responses = cr.Select(y => new { id = y.Id, text = y.Response }).ToList() })
.ToJson() .ToJson()
.ToStream() .ToStream()
.ConfigureAwait(false); .ConfigureAwait(false);
@ -183,9 +196,9 @@ namespace NadekoBot.Modules.CustomReactions
customReactions = GuildReactions.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet<CustomReaction>()); customReactions = GuildReactions.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet<CustomReaction>());
if (customReactions == null || !customReactions.Any()) if (customReactions == null || !customReactions.Any())
await imsg.Channel.SendMessageAsync("`No custom reactions found`").ConfigureAwait(false); await imsg.Channel.SendErrorAsync("No custom reactions found").ConfigureAwait(false);
else else
await imsg.Channel.SendMessageAsync($"{imsg.Author.Mention}\n`Page {page} of custom reactions (grouped):`\n" + await imsg.Channel.SendConfirmAsync($"Page {page} of custom reactions (grouped):",
string.Join("\r\n", customReactions string.Join("\r\n", customReactions
.GroupBy(cr => cr.Trigger) .GroupBy(cr => cr.Trigger)
.OrderBy(cr => cr.Key) .OrderBy(cr => cr.Key)
@ -209,11 +222,14 @@ namespace NadekoBot.Modules.CustomReactions
var found = customReactions.FirstOrDefault(cr => cr.Id == id); var found = customReactions.FirstOrDefault(cr => cr.Id == id);
if (found == null) if (found == null)
await imsg.Channel.SendMessageAsync("`No custom reaction found with that id.`").ConfigureAwait(false); await imsg.Channel.SendErrorAsync("No custom reaction found with that id.").ConfigureAwait(false);
else else
{ {
await imsg.Channel.SendMessageAsync($"`Custom reaction #{id}`\n`Trigger:` {found.Trigger}\n`Response:` {found.Response} ```css\n{found.Response}```") await imsg.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
.ConfigureAwait(false); .WithDescription($"#{id}")
.AddField(efb => efb.WithName("Trigger").WithValue(found.Trigger))
.AddField(efb => efb.WithName("Response").WithValue(found.Response + "\n```css\n" + found.Response + "```"))
.Build()).ConfigureAwait(false);
} }
} }
@ -224,7 +240,7 @@ namespace NadekoBot.Modules.CustomReactions
if ((channel == null && !NadekoBot.Credentials.IsOwner(imsg.Author)) || (channel != null && !((IGuildUser)imsg.Author).GuildPermissions.Administrator)) if ((channel == null && !NadekoBot.Credentials.IsOwner(imsg.Author)) || (channel != null && !((IGuildUser)imsg.Author).GuildPermissions.Administrator))
{ {
try { await imsg.Channel.SendMessageAsync("Insufficient permissions. Requires Bot ownership for global custom reactions, and Administrator for guild custom reactions."); } catch { } try { await imsg.Channel.SendErrorAsync("Insufficient permissions. Requires Bot ownership for global custom reactions, and Administrator for guild custom reactions."); } catch { }
return; return;
} }
@ -253,9 +269,45 @@ namespace NadekoBot.Modules.CustomReactions
} }
if (success) if (success)
await imsg.Channel.SendMessageAsync("**Successfully deleted custom reaction** " + toDelete.ToString()).ConfigureAwait(false); await imsg.Channel.SendConfirmAsync("Deleted custom reaction", toDelete.ToString()).ConfigureAwait(false);
else else
await imsg.Channel.SendMessageAsync("`Failed to find that custom reaction.`").ConfigureAwait(false); await imsg.Channel.SendErrorAsync("Failed to find that custom reaction.").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
public async Task CrStatsClear(IUserMessage imsg, string trigger = null)
{
if (string.IsNullOrWhiteSpace(trigger))
{
ClearStats();
await imsg.Channel.SendConfirmAsync($"Custom reaction stats cleared.").ConfigureAwait(false);
}
else
{
uint throwaway;
if (ReactionStats.TryRemove(trigger, out throwaway))
{
await imsg.Channel.SendConfirmAsync($"Stats cleared for `{trigger}` custom reaction.").ConfigureAwait(false);
}
else
{
await imsg.Channel.SendErrorAsync("No stats for that trigger found, no action taken.").ConfigureAwait(false);
}
}
}
[NadekoCommand, Usage, Description, Aliases]
public async Task CrStats(IUserMessage imsg, int page = 1)
{
if (page < 1)
return;
await imsg.Channel.EmbedAsync(ReactionStats.OrderByDescending(x => x.Value)
.Skip((page - 1) * 9)
.Take(9)
.Aggregate(new EmbedBuilder().WithOkColor().WithTitle($"Custom Reaction stats page #{page}"),
(agg, cur) => agg.AddField(efb => efb.WithName(cur.Key).WithValue(cur.Value.ToString()).WithIsInline(true)))
.Build())
.ConfigureAwait(false);
} }
} }
} }

View File

@ -1,8 +1,11 @@
using Discord; using Discord;
using NadekoBot.Extensions;
using NadekoBot.Services; using NadekoBot.Services;
using NadekoBot.Services.Database.Models; using NadekoBot.Services.Database.Models;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
namespace NadekoBot.Modules.CustomReactions namespace NadekoBot.Modules.CustomReactions
{ {
@ -17,7 +20,41 @@ namespace NadekoBot.Modules.CustomReactions
{ {
{"%mention%", (ctx) => { return $"<@{NadekoBot.Client.GetCurrentUser().Id}>"; } }, {"%mention%", (ctx) => { return $"<@{NadekoBot.Client.GetCurrentUser().Id}>"; } },
{"%user%", (ctx) => { return ctx.Author.Mention; } }, {"%user%", (ctx) => { return ctx.Author.Mention; } },
{"%rng%", (ctx) => { return new NadekoRandom().Next(0,10).ToString(); } } {"%rnduser%", (ctx) => {
var ch = ctx.Channel as ITextChannel;
if(ch == null)
return "";
var usrs = (ch.Guild.GetUsersAsync().GetAwaiter().GetResult());
return usrs.Skip(new NadekoRandom().Next(0,usrs.Count-1)).Shuffle().FirstOrDefault()?.Mention ?? "";
} }
//{"%rng%", (ctx) => { return new NadekoRandom().Next(0,10).ToString(); } }
};
private static readonly Regex rngRegex = new Regex("%rng(?:(?<from>(?:-)?\\d+)-(?<to>(?:-)?\\d+))?%", RegexOptions.Compiled);
private static readonly NadekoRandom rng = new NadekoRandom();
public static Dictionary<Regex, MatchEvaluator> regexPlaceholders = new Dictionary<Regex, MatchEvaluator>()
{
{ rngRegex, (match) => {
int from = 0;
int.TryParse(match.Groups["from"].ToString(), out from);
int to = 0;
int.TryParse(match.Groups["to"].ToString(), out to);
if(from == 0 && to == 0)
{
return rng.Next(0, 11).ToString();
}
if(from >= to)
return "";
return rng.Next(from,to+1).ToString();
} }
}; };
private static string ResolveTriggerString(this string str, IUserMessage ctx) private static string ResolveTriggerString(this string str, IUserMessage ctx)
@ -40,6 +77,11 @@ namespace NadekoBot.Modules.CustomReactions
{ {
str = str.Replace(ph.Key.ToLowerInvariant(), ph.Value(ctx, resolvedTrigger)); str = str.Replace(ph.Key.ToLowerInvariant(), ph.Value(ctx, resolvedTrigger));
} }
foreach (var ph in regexPlaceholders)
{
str = ph.Key.Replace(str, ph.Value);
}
return str; return str;
} }

View File

@ -6,13 +6,10 @@ namespace NadekoBot.Modules
{ {
public class DiscordModule public class DiscordModule
{ {
protected ILocalization _l { get; }
protected CommandService _commands { get; }
protected ShardedDiscordClient _client { get; }
protected Logger _log { get; } protected Logger _log { get; }
protected string _prefix { get; } protected string _prefix { get; }
public DiscordModule(ILocalization loc, CommandService cmds, ShardedDiscordClient client) public DiscordModule()
{ {
string prefix; string prefix;
if (NadekoBot.ModulePrefixes.TryGetValue(this.GetType().Name, out prefix)) if (NadekoBot.ModulePrefixes.TryGetValue(this.GetType().Name, out prefix))
@ -20,9 +17,6 @@ namespace NadekoBot.Modules
else else
_prefix = "?missing_prefix?"; _prefix = "?missing_prefix?";
_l = loc;
_commands = cmds;
_client = client;
_log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
} }
} }

View File

@ -18,11 +18,7 @@ namespace NadekoBot.Modules.Gambling
[Group] [Group]
public class AnimalRacing public class AnimalRacing
{ {
public static ConcurrentDictionary<ulong, AnimalRace> AnimalRaces { get; } = new ConcurrentDictionary<ulong, AnimalRace>();
public AnimalRacing()
{
}
public static ConcurrentDictionary<ulong, AnimalRace> AnimalRaces = new ConcurrentDictionary<ulong, AnimalRace>();
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
@ -33,7 +29,7 @@ namespace NadekoBot.Modules.Gambling
var ar = new AnimalRace(channel.Guild.Id, channel); var ar = new AnimalRace(channel.Guild.Id, channel);
if (ar.Fail) if (ar.Fail)
await channel.SendMessageAsync("🏁 `Failed starting a race. Another race is probably running.`"); await channel.SendErrorAsync("Animal Race", "Failed starting. Another race is probably running.").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -49,7 +45,7 @@ namespace NadekoBot.Modules.Gambling
AnimalRace ar; AnimalRace ar;
if (!AnimalRaces.TryGetValue(channel.Guild.Id, out ar)) if (!AnimalRaces.TryGetValue(channel.Guild.Id, out ar))
{ {
await channel.SendMessageAsync("No race exists on this server"); await channel.SendErrorAsync("No race exists on this server").ConfigureAwait(false);
return; return;
} }
await ar.JoinRace(umsg.Author as IGuildUser, amount); await ar.JoinRace(umsg.Author as IGuildUser, amount);
@ -94,21 +90,29 @@ namespace NadekoBot.Modules.Gambling
{ {
try try
{ {
try { await raceChannel.SendMessageAsync($"🏁`Race is starting in 20 seconds or when the room is full. Type {NadekoBot.ModulePrefixes[typeof(Gambling).Name]}jr to join the race.`"); } catch (Exception ex) { _log.Warn(ex); } 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.");
}
catch (Exception ex)
{
_log.Warn(ex);
}
var t = await Task.WhenAny(Task.Delay(20000, token), fullgame); var t = await Task.WhenAny(Task.Delay(20000, token), fullgame);
Started = true; Started = true;
cancelSource.Cancel(); cancelSource.Cancel();
if (t == fullgame) if (t == fullgame)
{ {
try { await raceChannel.SendMessageAsync("🏁`Race full, starting right now!`"); } catch (Exception ex) { _log.Warn(ex); } try { await raceChannel.SendConfirmAsync("Animal Race", "Full! Starting immediately."); } catch (Exception ex) { _log.Warn(ex); }
} }
else if (participants.Count > 1) else if (participants.Count > 1)
{ {
try { await raceChannel.SendMessageAsync("🏁`Game starting with " + participants.Count + " participants.`"); } catch (Exception ex) { _log.Warn(ex); } try { await raceChannel.SendConfirmAsync("Animal Race", "Starting with " + participants.Count + " participants."); } catch (Exception ex) { _log.Warn(ex); }
} }
else else
{ {
try { await raceChannel.SendMessageAsync("🏁`Race failed to start since there was not enough participants.`"); } catch (Exception ex) { _log.Warn(ex); } 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(); var p = participants.FirstOrDefault();
if (p != null && p.AmountBet > 0) if (p != null && p.AmountBet > 0)
@ -119,7 +123,7 @@ namespace NadekoBot.Modules.Gambling
await Task.Run(StartRace); await Task.Run(StartRace);
End(); End();
} }
catch { } catch { try { End(); } catch { } }
}); });
} }
@ -145,13 +149,20 @@ namespace NadekoBot.Modules.Gambling
participants.ForEach(p => participants.ForEach(p =>
{ {
p.Total += 1 + rng.Next(0, 10); p.Total += 1 + rng.Next(0, 10);
});
participants
.OrderByDescending(p => p.Total)
.ForEach(p =>
{
if (p.Total > 60) if (p.Total > 60)
{ {
p.Total = 60;
if (winner == null) if (winner == null)
{ {
winner = p; winner = p;
} }
p.Total = 60;
if (p.Place == 0) if (p.Place == 0)
p.Place = place++; p.Place = place++;
} }
@ -188,25 +199,25 @@ namespace NadekoBot.Modules.Gambling
{ {
var wonAmount = winner.AmountBet * (participants.Count - 1); var wonAmount = winner.AmountBet * (participants.Count - 1);
await CurrencyHandler.AddCurrencyAsync(winner.User, "Won a Race", wonAmount, false).ConfigureAwait(false); await CurrencyHandler.AddCurrencyAsync(winner.User, "Won a Race", wonAmount, true).ConfigureAwait(false);
await raceChannel.SendMessageAsync($"🏁 {winner.User.Mention} as {winner.Animal} **Won the race and {wonAmount}{CurrencySign}!**").ConfigureAwait(false); await raceChannel.SendConfirmAsync("Animal Race", $"{winner.User.Mention} as {winner.Animal} **Won the race and {wonAmount}{CurrencySign}!**").ConfigureAwait(false);
} }
else else
{ {
await raceChannel.SendMessageAsync($"🏁 {winner.User.Mention} as {winner.Animal} **Won the race!**"); await raceChannel.SendConfirmAsync("Animal Race", $"{winner.User.Mention} as {winner.Animal} **Won the race!**").ConfigureAwait(false);
} }
} }
private Task Client_MessageReceived(IMessage imsg) private void Client_MessageReceived(IMessage imsg)
{ {
var msg = imsg as IUserMessage; var msg = imsg as IUserMessage;
if (msg == null) if (msg == null)
return Task.CompletedTask; return;
if (msg.IsAuthor() || !(imsg.Channel is ITextChannel) || imsg.Channel != raceChannel) if (msg.IsAuthor() || !(imsg.Channel is ITextChannel) || imsg.Channel != raceChannel)
return Task.CompletedTask; return;
messagesSinceGameStarted++; messagesSinceGameStarted++;
return Task.CompletedTask; return;
} }
private async Task CheckForFullGameAsync(CancellationToken cancelToken) private async Task CheckForFullGameAsync(CancellationToken cancelToken)
@ -222,28 +233,29 @@ namespace NadekoBot.Modules.Gambling
var animal = ""; var animal = "";
if (!animals.TryDequeue(out animal)) if (!animals.TryDequeue(out animal))
{ {
await raceChannel.SendMessageAsync($"{u.Mention} `There is no running race on this server.`"); await raceChannel.SendErrorAsync($"{u.Mention} `There is no running race on this server.`").ConfigureAwait(false);
return; return;
} }
var p = new Participant(u, animal, amount); var p = new Participant(u, animal, amount);
if (participants.Contains(p)) if (participants.Contains(p))
{ {
await raceChannel.SendMessageAsync($"{u.Mention} `You already joined this race.`"); await raceChannel.SendErrorAsync($"{u.Mention} `You already joined this race.`").ConfigureAwait(false);
return; return;
} }
if (Started) if (Started)
{ {
await raceChannel.SendMessageAsync($"{u.Mention} `Race is already started`"); await raceChannel.SendErrorAsync($"{u.Mention} `Race is already started`").ConfigureAwait(false);
return; return;
} }
if (amount > 0) if (amount > 0)
if (!await CurrencyHandler.RemoveCurrencyAsync((IGuildUser)u, "BetRace", amount, true).ConfigureAwait(false)) if (!await CurrencyHandler.RemoveCurrencyAsync((IGuildUser)u, "BetRace", amount, false).ConfigureAwait(false))
{ {
try { await raceChannel.SendMessageAsync($"{u.Mention} You don't have enough {Gambling.CurrencyName}s.").ConfigureAwait(false); } catch { } try { await raceChannel.SendErrorAsync($"{u.Mention} You don't have enough {Gambling.CurrencyName}s.").ConfigureAwait(false); } catch { }
return; return;
} }
participants.Add(p); participants.Add(p);
await raceChannel.SendMessageAsync($"{u.Mention} **joined the race as a {p.Animal}" + (amount > 0 ? $" and bet {amount} {CurrencySign}!**" : "**")); await raceChannel.SendConfirmAsync("Animal Race", $"{u.Mention} **joined as a {p.Animal}" + (amount > 0 ? $" and bet {amount} {CurrencySign}!**" : "**"))
.ConfigureAwait(false);
} }
} }
@ -265,10 +277,7 @@ namespace NadekoBot.Modules.Gambling
this.AmountBet = amount; this.AmountBet = amount;
} }
public override int GetHashCode() public override int GetHashCode() => User.GetHashCode();
{
return User.GetHashCode();
}
public override bool Equals(object obj) public override bool Equals(object obj)
{ {

View File

@ -19,6 +19,9 @@ namespace NadekoBot.Modules.Gambling
public class DriceRollCommands public class DriceRollCommands
{ {
private Regex dndRegex { get; } = new Regex(@"^(?<n1>\d+)d(?<n2>\d+)(?:\+(?<add>\d+))?(?:\-(?<sub>\d+))?$", RegexOptions.Compiled); 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[] { '-', ' ', '+' };
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
@ -46,57 +49,55 @@ namespace NadekoBot.Modules.Gambling
await channel.SendFileAsync(imageStream, "dice.png", $"{umsg.Author.Mention} rolled " + Format.Code(gen.ToString())).ConfigureAwait(false); await channel.SendFileAsync(imageStream, "dice.png", $"{umsg.Author.Mention} rolled " + Format.Code(gen.ToString())).ConfigureAwait(false);
} }
//todo merge into internallDndRoll and internalRoll
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[Priority(1)]
public async Task Roll(IUserMessage umsg, string arg)
{
var channel = (ITextChannel)umsg.Channel;
if (channel == null)
return;
var ordered = true; public enum RollOrderType
var rng = new NadekoRandom();
Match match;
if ((match = dndRegex.Match(arg)).Length != 0)
{ {
int n1; Ordered,
int n2; Unordered
if (int.TryParse(match.Groups["n1"].ToString(), out n1) &&
int.TryParse(match.Groups["n2"].ToString(), out n2) &&
n1 <= 50 && n2 <= 100000 && n1 > 0 && n2 > 0)
{
var add = 0;
var sub = 0;
int.TryParse(match.Groups["add"].Value, out add);
int.TryParse(match.Groups["sub"].Value, out sub);
var arr = new int[n1];
for (int i = 0; i < n1; i++)
{
arr[i] = rng.Next(1, n2 + 1) + add - sub;
}
var elemCnt = 0;
await channel.SendMessageAsync($"{umsg.Author.Mention} rolled {n1} {(n1 == 1 ? "die" : "dice")} `1 to {n2}` +`{add}` -`{sub}`.\n`Result:` " + string.Join(", ", (ordered ? arr.OrderBy(x => x).AsEnumerable() : arr).Select(x => elemCnt++ % 2 == 0 ? $"**{x}**" : x.ToString()))).ConfigureAwait(false);
}
}
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[Priority(0)] [Priority(0)]
public async Task Roll(IUserMessage umsg, int num) public async Task Roll(IUserMessage umsg, int num)
{
await InternalRoll(umsg, num, true).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[Priority(0)]
public async Task Rolluo(IUserMessage umsg, int num)
{
await InternalRoll(umsg, num, false).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[Priority(1)]
public async Task Roll(IUserMessage umsg, string arg)
{
await InternallDndRoll(umsg, arg, true).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[Priority(1)]
public async Task Rolluo(IUserMessage umsg, string arg)
{
await InternallDndRoll(umsg, arg, false).ConfigureAwait(false);
}
private async Task InternalRoll(IUserMessage umsg, int num, bool ordered)
{ {
var channel = (ITextChannel)umsg.Channel; var channel = (ITextChannel)umsg.Channel;
if (channel == null) if (channel == null)
return; return;
var ordered = true;
if (num < 1 || num > 30) if (num < 1 || num > 30)
{ {
await channel.SendMessageAsync("Invalid number specified. You can roll up to 1-30 dice at a time.").ConfigureAwait(false); await channel.SendErrorAsync("Invalid number specified. You can roll up to 1-30 dice at a time.").ConfigureAwait(false);
return; return;
} }
@ -137,21 +138,35 @@ namespace NadekoBot.Modules.Gambling
await channel.SendFileAsync(ms, "dice.png", $"{umsg.Author.Mention} rolled {values.Count} {(values.Count == 1 ? "die" : "dice")}. Total: **{values.Sum()}** Average: **{(values.Sum() / (1.0f * values.Count)).ToString("N2")}**").ConfigureAwait(false); await channel.SendFileAsync(ms, "dice.png", $"{umsg.Author.Mention} rolled {values.Count} {(values.Count == 1 ? "die" : "dice")}. Total: **{values.Sum()}** Average: **{(values.Sum() / (1.0f * values.Count)).ToString("N2")}**").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] private async Task InternallDndRoll(IUserMessage umsg, string arg, bool ordered)
[RequireContext(ContextType.Guild)]
public async Task Rolluo(IUserMessage umsg, string arg)
{ {
var channel = (ITextChannel)umsg.Channel; var channel = (ITextChannel)umsg.Channel;
if (channel == null) if (channel == null)
return; return;
var ordered = false;
var rng = new NadekoRandom();
Match match; Match match;
if ((match = dndRegex.Match(arg)).Length != 0)
{
int n1; int n1;
int n2; int n2;
if ((match = fudgeRegex.Match(arg)).Length != 0 &&
int.TryParse(match.Groups["n1"].ToString(), out n1) &&
n1 > 0 && n1 < 500)
{
var rng = new NadekoRandom();
var rolls = new List<char>();
for (int i = 0; i < n1; i++)
{
rolls.Add(fateRolls[rng.Next(0, fateRolls.Length)]);
}
var embed = new EmbedBuilder().WithOkColor().WithDescription($"{umsg.Author.Mention} rolled {n1} fate {(n1 == 1 ? "die" : "dice")}.")
.AddField(efb => efb.WithName(Format.Bold("Result"))
.WithValue(string.Join(" ", rolls.Select(c => Format.Code($"[{c}]")))));
await channel.EmbedAsync(embed.Build()).ConfigureAwait(false);
}
else if ((match = dndRegex.Match(arg)).Length != 0)
{
var rng = new NadekoRandom();
if (int.TryParse(match.Groups["n1"].ToString(), out n1) && if (int.TryParse(match.Groups["n1"].ToString(), out n1) &&
int.TryParse(match.Groups["n2"].ToString(), out n2) && int.TryParse(match.Groups["n2"].ToString(), out n2) &&
n1 <= 50 && n2 <= 100000 && n1 > 0 && n2 > 0) n1 <= 50 && n2 <= 100000 && n1 > 0 && n2 > 0)
@ -166,64 +181,14 @@ namespace NadekoBot.Modules.Gambling
{ {
arr[i] = rng.Next(1, n2 + 1) + add - sub; arr[i] = rng.Next(1, n2 + 1) + add - sub;
} }
var elemCnt = 0;
await channel.SendMessageAsync($"{umsg.Author.Mention} rolled {n1} {(n1 == 1 ? "die" : "dice")} `1 to {n2}` +`{add}` -`{sub}`.\n`Result:` " + string.Join(", ", (ordered ? arr.OrderBy(x => x).AsEnumerable() : arr).Select(x => elemCnt++ % 2 == 0 ? $"**{x}**" : x.ToString()))).ConfigureAwait(false);
}
}
}
[NadekoCommand, Usage, Description, Aliases] var embed = new EmbedBuilder().WithOkColor().WithDescription($"{umsg.Author.Mention} rolled {n1} {(n1 == 1 ? "die" : "dice")} `1 to {n2}` +`{add}` -`{sub}`")
[RequireContext(ContextType.Guild)] .AddField(efb => efb.WithName(Format.Bold("Result"))
public async Task Rolluo(IUserMessage umsg, int num) .WithValue(string.Join(" ", (ordered ? arr.OrderBy(x => x).AsEnumerable() : arr).Select(x => Format.Code(x.ToString())))));
{ await channel.EmbedAsync(embed.Build()).ConfigureAwait(false);
var channel = (ITextChannel)umsg.Channel;
if (channel == null)
return;
var ordered = false;
if (num < 1 || num > 30)
{
await channel.SendMessageAsync("Invalid number specified. You can roll up to 1-30 dice at a time.").ConfigureAwait(false);
return;
}
var rng = new NadekoRandom();
var dice = new List<Image>(num);
var values = new List<int>(num);
for (var i = 0; i < num; i++)
{
var randomNumber = rng.Next(1, 7);
var toInsert = dice.Count;
if (ordered)
{
if (randomNumber == 6 || dice.Count == 0)
toInsert = 0;
else if (randomNumber != 1)
for (var j = 0; j < dice.Count; j++)
{
if (values[j] < randomNumber)
{
toInsert = j;
break;
} }
} }
} }
else
{
toInsert = dice.Count;
}
dice.Insert(toInsert, GetDice(randomNumber));
values.Insert(toInsert, randomNumber);
}
var bitmap = dice.Merge();
var ms = new MemoryStream();
bitmap.SaveAsPng(ms);
ms.Position = 0;
await channel.SendFileAsync(ms, "dice.png", $"{umsg.Author.Mention} rolled {values.Count} {(values.Count == 1 ? "die" : "dice")}. Total: **{values.Sum()}** Average: **{(values.Sum() / (1.0f * values.Count)).ToString("N2")}**").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
@ -241,7 +206,7 @@ namespace NadekoBot.Modules.Gambling
.Select(int.Parse) .Select(int.Parse)
.ToArray(); .ToArray();
if (arr[0] > arr[1]) if (arr[0] > arr[1])
throw new ArgumentException("First argument should be bigger than the second one."); throw new ArgumentException("Second argument must be larger than the first one.");
rolled = new NadekoRandom().Next(arr[0], arr[1] + 1); rolled = new NadekoRandom().Next(arr[0], arr[1] + 1);
} }
else else
@ -249,11 +214,11 @@ namespace NadekoBot.Modules.Gambling
rolled = new NadekoRandom().Next(0, int.Parse(range) + 1); rolled = new NadekoRandom().Next(0, int.Parse(range) + 1);
} }
await channel.SendMessageAsync($"{umsg.Author.Mention} rolled **{rolled}**.").ConfigureAwait(false); await channel.SendConfirmAsync($"{umsg.Author.Mention} rolled **{rolled}**.").ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {
await channel.SendMessageAsync($":anger: {ex.Message}").ConfigureAwait(false); await channel.SendErrorAsync($":anger: {ex.Message}").ConfigureAwait(false);
} }
} }

View File

@ -42,7 +42,7 @@ namespace NadekoBot.Modules.Gambling
{ {
if (cards.CardPool.Count == 0 && i != 0) if (cards.CardPool.Count == 0 && i != 0)
{ {
try { await channel.SendMessageAsync("No more cards in a deck.").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); } try { await channel.SendErrorAsync("No more cards in a deck.").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
break; break;
} }
var currentCard = cards.DrawACard(); var currentCard = cards.DrawACard();
@ -53,7 +53,7 @@ namespace NadekoBot.Modules.Gambling
MemoryStream bitmapStream = new MemoryStream(); MemoryStream bitmapStream = new MemoryStream();
images.Merge().SaveAsPng(bitmapStream); images.Merge().SaveAsPng(bitmapStream);
bitmapStream.Position = 0; bitmapStream.Position = 0;
//todo CARD NAMES?
var toSend = $"{msg.Author.Mention}"; var toSend = $"{msg.Author.Mention}";
if (cardObjects.Count == 5) if (cardObjects.Count == 5)
toSend += $" drew `{Cards.GetHandValue(cardObjects)}`"; toSend += $" drew `{Cards.GetHandValue(cardObjects)}`";
@ -75,7 +75,7 @@ namespace NadekoBot.Modules.Gambling
return c; return c;
}); });
await channel.SendMessageAsync("`Deck reshuffled.`").ConfigureAwait(false); await channel.SendConfirmAsync("Deck reshuffled.").ConfigureAwait(false);
} }
} }
} }

View File

@ -15,10 +15,9 @@ namespace NadekoBot.Modules.Gambling
[Group] [Group]
public class FlipCoinCommands public class FlipCoinCommands
{ {
NadekoRandom rng { get; } = new NadekoRandom(); private static NadekoRandom rng { get; } = new NadekoRandom();
private const string headsPath = "data/images/coins/heads.png"; private const string headsPath = "data/images/coins/heads.png";
private const string tailsPath = "data/images/coins/tails.png"; private const string tailsPath = "data/images/coins/tails.png";
public FlipCoinCommands() { }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
@ -28,14 +27,14 @@ namespace NadekoBot.Modules.Gambling
if (count == 1) if (count == 1)
{ {
if (rng.Next(0, 2) == 1) if (rng.Next(0, 2) == 1)
await channel.SendFileAsync(headsPath, $"{imsg.Author.Mention} rolled " + Format.Code("Heads") + ".").ConfigureAwait(false); await channel.SendFileAsync(headsPath, $"{imsg.Author.Mention} flipped " + Format.Code("Heads") + ".").ConfigureAwait(false);
else else
await channel.SendFileAsync(tailsPath, $"{imsg.Author.Mention} rolled " + Format.Code("Tails") + ".").ConfigureAwait(false); await channel.SendFileAsync(tailsPath, $"{imsg.Author.Mention} flipped " + Format.Code("Tails") + ".").ConfigureAwait(false);
return; return;
} }
if (count > 10 || count < 1) if (count > 10 || count < 1)
{ {
await channel.SendMessageAsync("`Invalid number specified. You can flip 1 to 10 coins.`"); await channel.SendErrorAsync("`Invalid number specified. You can flip 1 to 10 coins.`");
return; return;
} }
var imgs = new Image[count]; var imgs = new Image[count];
@ -60,24 +59,16 @@ namespace NadekoBot.Modules.Gambling
if (amount < 3) if (amount < 3)
{ {
await channel.SendMessageAsync($"You can't bet less than 3{Gambling.CurrencySign}.") await channel.SendErrorAsync($"You can't bet less than 3{Gambling.CurrencySign}.")
.ConfigureAwait(false); .ConfigureAwait(false);
return; return;
} }
// todo update this var removed = await CurrencyHandler.RemoveCurrencyAsync(guildUser, "Betflip Gamble", amount, false).ConfigureAwait(false);
long userFlowers; if (!removed)
using (var uow = DbHandler.UnitOfWork())
{ {
userFlowers = uow.Currency.GetOrCreate(umsg.Author.Id).Amount; await channel.SendErrorAsync($"{guildUser.Mention} You don't have enough {Gambling.CurrencyPluralName}.").ConfigureAwait(false);
}
if (userFlowers < amount)
{
await channel.SendMessageAsync($"{umsg.Author.Mention} You don't have enough {Gambling.CurrencyPluralName}. You only have {userFlowers}{Gambling.CurrencySign}.").ConfigureAwait(false);
return; return;
} }
await CurrencyHandler.RemoveCurrencyAsync(guildUser, "Betflip Gamble", amount, false).ConfigureAwait(false);
//heads = true //heads = true
//tails = false //tails = false

View File

@ -20,7 +20,7 @@ namespace NadekoBot.Modules.Gambling
public static string CurrencyPluralName { get; set; } public static string CurrencyPluralName { get; set; }
public static string CurrencySign { get; set; } public static string CurrencySign { get; set; }
public Gambling(ILocalization loc, CommandService cmds, ShardedDiscordClient client) : base(loc, cmds, client) public Gambling() : base()
{ {
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
@ -51,7 +51,7 @@ namespace NadekoBot.Modules.Gambling
var members = role.Members().Where(u => u.Status != UserStatus.Offline && u.Status != UserStatus.Unknown); var members = role.Members().Where(u => u.Status != UserStatus.Offline && u.Status != UserStatus.Unknown);
var membersArray = members as IUser[] ?? members.ToArray(); var membersArray = members as IUser[] ?? members.ToArray();
var usr = membersArray[new NadekoRandom().Next(0, membersArray.Length)]; var usr = membersArray[new NadekoRandom().Next(0, membersArray.Length)];
await channel.SendMessageAsync($"🎟 Raffled user: **{usr.Username}#{usr.Discriminator}** ID: `{usr.Id}`").ConfigureAwait(false); await channel.SendConfirmAsync("🎟 Raffled user", $"**{usr.Username}#{usr.Discriminator}** ID: `{usr.Id}`").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -62,7 +62,7 @@ namespace NadekoBot.Modules.Gambling
user = user ?? umsg.Author; user = user ?? umsg.Author;
await channel.SendMessageAsync($"{user.Username} has {GetCurrency(user.Id)} {CurrencySign}").ConfigureAwait(false); await channel.SendConfirmAsync($"{user.Username} has {GetCurrency(user.Id)} {CurrencySign}").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -71,7 +71,7 @@ namespace NadekoBot.Modules.Gambling
{ {
var channel = umsg.Channel; var channel = umsg.Channel;
await channel.SendMessageAsync($"`{userId}` has {GetCurrency(userId)} {CurrencySign}").ConfigureAwait(false); await channel.SendConfirmAsync($"`{userId}` has {GetCurrency(userId)} {CurrencySign}").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -84,11 +84,11 @@ namespace NadekoBot.Modules.Gambling
var success = await CurrencyHandler.RemoveCurrencyAsync((IGuildUser)umsg.Author, $"Gift to {receiver.Username} ({receiver.Id}).", amount, true).ConfigureAwait(false); var success = await CurrencyHandler.RemoveCurrencyAsync((IGuildUser)umsg.Author, $"Gift to {receiver.Username} ({receiver.Id}).", amount, true).ConfigureAwait(false);
if (!success) if (!success)
{ {
await channel.SendMessageAsync($"{umsg.Author.Mention} You don't have enough {Gambling.CurrencyPluralName}.").ConfigureAwait(false); await channel.SendErrorAsync($"{umsg.Author.Mention} You don't have enough {Gambling.CurrencyPluralName}.").ConfigureAwait(false);
return; return;
} }
await CurrencyHandler.AddCurrencyAsync(receiver, $"Gift from {umsg.Author.Username} ({umsg.Author.Id}).", amount, true).ConfigureAwait(false); await CurrencyHandler.AddCurrencyAsync(receiver, $"Gift from {umsg.Author.Username} ({umsg.Author.Id}).", amount, true).ConfigureAwait(false);
await channel.SendMessageAsync($"{umsg.Author.Mention} successfully sent {amount} {(amount == 1 ? Gambling.CurrencyName : Gambling.CurrencyPluralName)} to {receiver.Mention}!").ConfigureAwait(false); await channel.SendConfirmAsync($"{umsg.Author.Mention} successfully sent {amount} {(amount == 1 ? Gambling.CurrencyName : Gambling.CurrencyPluralName)} to {receiver.Mention}!").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -111,7 +111,7 @@ namespace NadekoBot.Modules.Gambling
await CurrencyHandler.AddCurrencyAsync(usrId, $"Awarded by bot owner. ({umsg.Author.Username}/{umsg.Author.Id})", amount).ConfigureAwait(false); await CurrencyHandler.AddCurrencyAsync(usrId, $"Awarded by bot owner. ({umsg.Author.Username}/{umsg.Author.Id})", amount).ConfigureAwait(false);
await channel.SendMessageAsync($"{umsg.Author.Mention} successfully awarded {amount} {(amount == 1 ? Gambling.CurrencyName : Gambling.CurrencyPluralName)} to <@{usrId}>!").ConfigureAwait(false); await channel.SendConfirmAsync($"{umsg.Author.Mention} successfully awarded {amount} {(amount == 1 ? Gambling.CurrencyName : Gambling.CurrencyPluralName)} to <@{usrId}>!").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -129,7 +129,7 @@ namespace NadekoBot.Modules.Gambling
amount))) amount)))
.ConfigureAwait(false); .ConfigureAwait(false);
await channel.SendMessageAsync($"Awarded `{amount}` {Gambling.CurrencyPluralName} to `{users.Count}` users from `{role.Name}` role.") await channel.SendConfirmAsync($"Awarded `{amount}` {Gambling.CurrencyPluralName} to `{users.Count}` users from `{role.Name}` role.")
.ConfigureAwait(false); .ConfigureAwait(false);
} }
@ -144,9 +144,9 @@ namespace NadekoBot.Modules.Gambling
return; return;
if(await CurrencyHandler.RemoveCurrencyAsync(user, $"Taken by bot owner.({umsg.Author.Username}/{umsg.Author.Id})", amount, true).ConfigureAwait(false)) if(await CurrencyHandler.RemoveCurrencyAsync(user, $"Taken by bot owner.({umsg.Author.Username}/{umsg.Author.Id})", amount, true).ConfigureAwait(false))
await channel.SendMessageAsync($"{umsg.Author.Mention} successfully took {amount} {(amount == 1? Gambling.CurrencyName : Gambling.CurrencyPluralName)} from {user}!").ConfigureAwait(false); await channel.SendConfirmAsync($"{umsg.Author.Mention} successfully took {amount} {(amount == 1? Gambling.CurrencyName : Gambling.CurrencyPluralName)} from {user}!").ConfigureAwait(false);
else else
await channel.SendMessageAsync($"{umsg.Author.Mention} was unable to take {amount} {(amount == 1 ? Gambling.CurrencyName : Gambling.CurrencyPluralName)} from {user} because the user doesn't have that much {Gambling.CurrencyPluralName}!").ConfigureAwait(false); await channel.SendErrorAsync($"{umsg.Author.Mention} was unable to take {amount} {(amount == 1 ? Gambling.CurrencyName : Gambling.CurrencyPluralName)} from {user} because the user doesn't have that much {Gambling.CurrencyPluralName}!").ConfigureAwait(false);
} }
@ -160,9 +160,9 @@ namespace NadekoBot.Modules.Gambling
return; return;
if(await CurrencyHandler.RemoveCurrencyAsync(usrId, $"Taken by bot owner.({umsg.Author.Username}/{umsg.Author.Id})", amount).ConfigureAwait(false)) if(await CurrencyHandler.RemoveCurrencyAsync(usrId, $"Taken by bot owner.({umsg.Author.Username}/{umsg.Author.Id})", amount).ConfigureAwait(false))
await channel.SendMessageAsync($"{umsg.Author.Mention} successfully took {amount} {(amount == 1 ? Gambling.CurrencyName : Gambling.CurrencyPluralName)} from <@{usrId}>!").ConfigureAwait(false); await channel.SendConfirmAsync($"{umsg.Author.Mention} successfully took {amount} {(amount == 1 ? Gambling.CurrencyName : Gambling.CurrencyPluralName)} from <@{usrId}>!").ConfigureAwait(false);
else else
await channel.SendMessageAsync($"{umsg.Author.Mention} was unable to take {amount} {(amount == 1 ? Gambling.CurrencyName : Gambling.CurrencyPluralName)} from `{usrId}` because the user doesn't have that much {Gambling.CurrencyPluralName}!").ConfigureAwait(false); await channel.SendErrorAsync($"{umsg.Author.Mention} was unable to take {amount} {(amount == 1 ? Gambling.CurrencyName : Gambling.CurrencyPluralName)} from `{usrId}` because the user doesn't have that much {Gambling.CurrencyPluralName}!").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -184,7 +184,7 @@ namespace NadekoBot.Modules.Gambling
if (userFlowers < amount) if (userFlowers < amount)
{ {
await channel.SendMessageAsync($"{guildUser.Mention} You don't have enough {Gambling.CurrencyPluralName}. You only have {userFlowers}{Gambling.CurrencySign}.").ConfigureAwait(false); await channel.SendErrorAsync($"{guildUser.Mention} You don't have enough {Gambling.CurrencyPluralName}. You only have {userFlowers}{Gambling.CurrencySign}.").ConfigureAwait(false);
return; return;
} }
@ -212,7 +212,7 @@ namespace NadekoBot.Modules.Gambling
await CurrencyHandler.AddCurrencyAsync(guildUser, "Betroll Gamble", amount * 10, false).ConfigureAwait(false); await CurrencyHandler.AddCurrencyAsync(guildUser, "Betroll Gamble", amount * 10, false).ConfigureAwait(false);
} }
await channel.SendMessageAsync(str).ConfigureAwait(false); await channel.SendConfirmAsync(str).ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -221,7 +221,7 @@ namespace NadekoBot.Modules.Gambling
{ {
var channel = (ITextChannel)umsg.Channel; var channel = (ITextChannel)umsg.Channel;
IEnumerable<Currency> richest; IEnumerable<Currency> richest = new List<Currency>();
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
richest = uow.Currency.GetTopRichest(10); richest = uow.Currency.GetTopRichest(10);
@ -235,7 +235,7 @@ $@"```xl
Id $$$ Id $$$
"), "),
(cur, cs) => cur.AppendLine($@"┣━━━━━━━━━━━━━━━━━━━━━╋━━━━━━━━┫ (cur, cs) => cur.AppendLine($@"┣━━━━━━━━━━━━━━━━━━━━━╋━━━━━━━━┫
{(channel.Guild.GetUser(cs.UserId)?.Username.TrimTo(18, true) ?? cs.UserId.ToString()),-20} {cs.Amount,6} ") {(channel.Guild.GetUser(cs.UserId)?.Username?.TrimTo(18, true) ?? cs.UserId.ToString()),-20} {cs.Amount,6} ")
).ToString() + "┗━━━━━━━━━━━━━━━━━━━━━┻━━━━━━━━┛```").ConfigureAwait(false); ).ToString() + "┗━━━━━━━━━━━━━━━━━━━━━┻━━━━━━━━┛```").ConfigureAwait(false);
} }
} }

View File

@ -0,0 +1,291 @@
using Discord;
using Discord.Commands;
using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Services;
using NLog;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace NadekoBot.Modules.Games
{
public partial class Games
{
[Group]
public class Acropobia
{
//channelId, game
public static ConcurrentDictionary<ulong, AcrophobiaGame> AcrophobiaGames { get; } = new ConcurrentDictionary<ulong, AcrophobiaGame>();
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Acro(IUserMessage imsg, int time = 60)
{
var channel = (ITextChannel)imsg.Channel;
var game = new AcrophobiaGame(channel, time);
if (AcrophobiaGames.TryAdd(channel.Id, game))
{
try
{
await game.Run();
}
finally
{
game.EnsureStopped();
AcrophobiaGames.TryRemove(channel.Id, out game);
}
}
else
{
await channel.SendErrorAsync("Acrophobia game is already running in this channel.").ConfigureAwait(false);
}
}
}
public enum AcroPhase
{
Submitting,
Idle, // used to wait for some other actions while transitioning through phases
Voting
}
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 AcroPhase phase { get; set; } = AcroPhase.Submitting;
private readonly ConcurrentDictionary<string, IGuildUser> submissions = new ConcurrentDictionary<string, IGuildUser>();
public IReadOnlyDictionary<string, IGuildUser> Submissions => submissions;
private readonly ConcurrentHashSet<ulong> usersWhoVoted = new ConcurrentHashSet<ulong>();
private int spamCount = 0;
//text, votes
private readonly ConcurrentDictionary<string, int> votes = new ConcurrentDictionary<string, int>();
private readonly Logger _log;
public AcrophobiaGame(ITextChannel channel, int time)
{
this._log = LogManager.GetCurrentClassLogger();
this.channel = channel;
this.time = time;
this.source = new CancellationTokenSource();
this.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;
}
startingLetters = lettersArr.ToImmutableArray();
}
private EmbedBuilder GetEmbed()
{
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("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"));
}
public async Task Run()
{
NadekoBot.Client.MessageReceived += PotentialAcro;
var embed = GetEmbed();
//SUBMISSIONS PHASE
await channel.EmbedAsync(embed.Build()).ConfigureAwait(false);
try
{
await Task.Delay(time * 1000, source.Token).ConfigureAwait(false);
phase = AcroPhase.Idle;
}
catch (OperationCanceledException)
{
return;
}
//var i = 0;
if (submissions.Count == 0)
{
await channel.SendErrorAsync("Acrophobia", "Game ended with no submissions.");
return;
}
else 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()))
.Build()).ConfigureAwait(false);
return;
}
var submissionClosedEmbed = GetEmbed();
await channel.EmbedAsync(submissionClosedEmbed.Build()).ConfigureAwait(false);
//VOTING PHASE
this.phase = AcroPhase.Voting;
try
{
//30 secondds for voting
await Task.Delay(30000, source.Token).ConfigureAwait(false);
this.phase = AcroPhase.Idle;
}
catch (OperationCanceledException)
{
return;
}
await End().ConfigureAwait(false);
}
private async void PotentialAcro(IMessage arg)
{
try
{
var msg = arg as IUserMessage;
if (msg == null || msg.Author.IsBot || msg.Channel.Id != channel.Id)
return;
++spamCount;
var guildUser = (IGuildUser)msg.Author;
var input = msg.Content.ToUpperInvariant().Trim();
if (phase == AcroPhase.Submitting)
{
if (spamCount > 10)
{
spamCount = 0;
try { await channel.EmbedAsync(GetEmbed().Build()).ConfigureAwait(false); }
catch { }
}
//user didn't input something already
IGuildUser throwaway;
if (submissions.TryGetValue(input, out throwaway))
return;
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
return;
for (int i = 0; i < startingLetters.Length; i++)
{
var letter = startingLetters[i];
if (!inputWords[i].StartsWith(letter.ToString())) // all first letters must match
return;
}
//try adding it to the list of answers
if (!submissions.TryAdd(input, guildUser))
return;
// all good. valid input. answer recorded
await channel.SendConfirmAsync("Acrophobia", $"{guildUser.Mention} submitted their sentence. ({submissions.Count} total)");
try
{
await msg.DeleteAsync();
}
catch
{
await msg.DeleteAsync(); //try twice
}
}
else if (phase == AcroPhase.Voting)
{
if (spamCount > 10)
{
spamCount = 0;
try { await channel.EmbedAsync(GetEmbed().Build()).ConfigureAwait(false); }
catch { }
}
IGuildUser usr;
//if (submissions.TryGetValue(input, out usr) && usr.Id != guildUser.Id)
//{
// if (!usersWhoVoted.Add(guildUser.Id))
// return;
// votes.AddOrUpdate(input, 1, (key, old) => ++old);
// await channel.SendConfirmAsync("Acrophobia", $"{guildUser.Mention} cast their vote!").ConfigureAwait(false);
// await msg.DeleteAsync().ConfigureAwait(false);
// return;
//}
int num;
if (int.TryParse(input, out num) && num > 0 && num <= submissions.Count)
{
var kvp = submissions.Skip(num - 1).First();
usr = kvp.Value;
//can't vote for yourself, can't vote multiple times
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);
await msg.DeleteAsync().ConfigureAwait(false);
return;
}
}
}
catch (Exception ex)
{
_log.Warn(ex);
}
}
public async Task End()
{
if (!votes.Any())
{
await channel.SendErrorAsync("Acrophobia", "No votes cast. Game ended with no winner.").ConfigureAwait(false);
return;
}
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")
.WithFooter(efb => efb.WithText(winner.Key.ToLowerInvariant().ToTitleCase()));
await channel.EmbedAsync(embed.Build()).ConfigureAwait(false);
}
public void EnsureStopped()
{
NadekoBot.Client.MessageReceived -= PotentialAcro;
if (!source.IsCancellationRequested)
source.Cancel();
}
}
}
}

View File

@ -1,6 +1,7 @@
using Discord; using Discord;
using Discord.Commands; using Discord.Commands;
using NadekoBot.Attributes; using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Services; using NadekoBot.Services;
using NLog; using NLog;
using Services.CleverBotApi; using Services.CleverBotApi;
@ -34,7 +35,7 @@ namespace NadekoBot.Modules.Games
{ {
var bot = ChatterBotFactory.Create(ChatterBotType.CLEVERBOT); var bot = ChatterBotFactory.Create(ChatterBotType.CLEVERBOT);
CleverbotGuilds = new ConcurrentDictionary<ulong, ChatterBotSession>( CleverbotGuilds = new ConcurrentDictionary<ulong, ChatterBotSession>(
uow.GuildConfigs.GetAll() NadekoBot.AllGuildConfigs
.Where(gc => gc.CleverbotEnabled) .Where(gc => gc.CleverbotEnabled)
.ToDictionary(gc => gc.GuildId, gc => bot.CreateSession())); .ToDictionary(gc => gc.GuildId, gc => bot.CreateSession()));
} }
@ -72,12 +73,11 @@ namespace NadekoBot.Modules.Games
var response = await cleverbot.Think(message).ConfigureAwait(false); var response = await cleverbot.Think(message).ConfigureAwait(false);
try try
{ {
await msg.Channel.SendMessageAsync(response).ConfigureAwait(false); await msg.Channel.SendConfirmAsync(response.SanitizeMentions()).ConfigureAwait(false);
} }
catch (Exception ex) catch
{ {
_log.Warn(ex, "Eror sending response"); await msg.Channel.SendConfirmAsync(response.SanitizeMentions()).ConfigureAwait(false); // try twice :\
await msg.Channel.SendMessageAsync(msg.Author.Mention+" "+response).ConfigureAwait(false); // try twice :\
} }
return true; return true;
} }
@ -97,7 +97,7 @@ namespace NadekoBot.Modules.Games
uow.GuildConfigs.SetCleverbotEnabled(channel.Guild.Id, false); uow.GuildConfigs.SetCleverbotEnabled(channel.Guild.Id, false);
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
} }
await channel.SendMessageAsync($"{imsg.Author.Mention} `Disabled cleverbot on this server.`").ConfigureAwait(false); await channel.SendConfirmAsync($"{imsg.Author.Mention} Disabled cleverbot on this server.").ConfigureAwait(false);
return; return;
} }
@ -112,7 +112,7 @@ namespace NadekoBot.Modules.Games
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
} }
await channel.SendMessageAsync($"{imsg.Author.Mention} `Enabled cleverbot on this server.`").ConfigureAwait(false); await channel.SendConfirmAsync($"{imsg.Author.Mention} Enabled cleverbot on this server.").ConfigureAwait(false);
} }
} }
} }

View File

@ -0,0 +1,215 @@
using Discord;
using NadekoBot.Extensions;
using NadekoBot.Services;
using Newtonsoft.Json;
using NLog;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NadekoBot.Modules.Games.Commands.Hangman
{
public class HangmanModel
{
public List<HangmanObject> All { get; set; }
public List<HangmanObject> Animals { get; set; }
public List<HangmanObject> Countries { get; set; }
public List<HangmanObject> Movies { get; set; }
public List<HangmanObject> Things { get; set; }
}
public class HangmanTermPool
{
public enum HangmanTermType
{
All,
Animals,
Countries,
Movies,
Things
}
const string termsPath = "data/hangman.json";
public static HangmanModel data { get; }
static HangmanTermPool()
{
try
{
data = JsonConvert.DeserializeObject<HangmanModel>(File.ReadAllText(termsPath));
data.All = data.Animals.Concat(data.Countries)
.Concat(data.Movies)
.Concat(data.Things)
.ToList();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
public static HangmanObject GetTerm(HangmanTermType type)
{
var rng = new NadekoRandom();
switch (type)
{
case HangmanTermType.Animals:
return data.Animals[rng.Next(0, data.Animals.Count)];
case HangmanTermType.Countries:
return data.Countries[rng.Next(0, data.Countries.Count)];
case HangmanTermType.Movies:
return data.Movies[rng.Next(0, data.Movies.Count)];
case HangmanTermType.Things:
return data.Things[rng.Next(0, data.Things.Count)];
default:
return data.All[rng.Next(0, data.All.Count)];
}
}
}
public class HangmanGame
{
private readonly Logger _log;
public IMessageChannel GameChannel { get; }
public HashSet<char> Guesses { get; } = new HashSet<char>();
public HangmanObject Term { get; private set; }
public uint Errors { get; private set; } = 0;
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)))
return $" {c}";
c = char.ToUpperInvariant(c);
if (c == ' ')
return " ";
return Guesses.Contains(c) ? $" {c}" : " _";
})) + "`";
public bool GuessedAll => Guesses.IsSupersetOf(Term.Word.ToUpperInvariant()
.Where(c => char.IsLetter(c) || char.IsDigit(c)));
public HangmanTermPool.HangmanTermType TermType { get; }
public event Action<HangmanGame> OnEnded;
public HangmanGame(IMessageChannel channel, HangmanTermPool.HangmanTermType type)
{
_log = LogManager.GetCurrentClassLogger();
this.GameChannel = channel;
this.TermType = type;
}
public void Start()
{
this.Term = HangmanTermPool.GetTerm(TermType);
// start listening for answers when game starts
NadekoBot.Client.MessageReceived += PotentialGuess;
}
public async Task End()
{
NadekoBot.Client.MessageReceived -= PotentialGuess;
OnEnded(this);
var toSend = "Game ended. You **" + (Errors >= MaxErrors ? "LOSE" : "WIN") + "**!\n" + GetHangman();
var embed = new EmbedBuilder().WithTitle("Hangman Game")
.WithDescription(toSend)
.AddField(efb => efb.WithName("It was").WithValue(Term.Word))
.WithImage(eib => eib.WithUrl(Term.ImageUrl))
.WithFooter(efb => efb.WithText(string.Join(" ", Guesses)));
if (Errors >= MaxErrors)
await GameChannel.EmbedAsync(embed.WithErrorColor().Build()).ConfigureAwait(false);
else
await GameChannel.EmbedAsync(embed.WithOkColor().Build()).ConfigureAwait(false);
}
private async void PotentialGuess(IMessage msg)
{
try
{
if (!(msg is IUserMessage))
return;
if (msg.Channel != GameChannel)
return; // message's channel has to be the same as game's
if (msg.Content.Length == 1) // message must be 1 char long
{
if (++MessagesSinceLastPost > 10)
{
MessagesSinceLastPost = 0;
try
{
await GameChannel.SendConfirmAsync("Hangman Game",
ScrambledWord + "\n" + GetHangman(),
footer: string.Join(" ", Guesses)).ConfigureAwait(false);
}
catch { }
}
if (!(char.IsLetter(msg.Content[0]) || char.IsDigit(msg.Content[0])))// and a letter or a digit
return;
var guess = char.ToUpperInvariant(msg.Content[0]);
if (Guesses.Contains(guess))
{
MessagesSinceLastPost = 0;
++Errors;
if (Errors < MaxErrors)
await GameChannel.SendErrorAsync("Hangman Game", $"{msg.Author.Mention} Letter `{guess}` has already been used.\n" + ScrambledWord + "\n" + GetHangman(),
footer: string.Join(" ", Guesses)).ConfigureAwait(false);
else
await End().ConfigureAwait(false);
return;
}
Guesses.Add(guess);
if (Term.Word.ToUpperInvariant().Contains(guess))
{
if (GuessedAll)
{
try { await GameChannel.SendConfirmAsync("Hangman Game", $"{msg.Author.Mention} guessed a letter `{guess}`!").ConfigureAwait(false); } catch { }
await End().ConfigureAwait(false);
return;
}
MessagesSinceLastPost = 0;
try
{
await GameChannel.SendConfirmAsync("Hangman Game", $"{msg.Author.Mention} guessed a letter `{guess}`!\n" + ScrambledWord + "\n" + GetHangman(),
footer: string.Join(" ", Guesses)).ConfigureAwait(false);
}
catch { }
}
else
{
MessagesSinceLastPost = 0;
++Errors;
if (Errors < MaxErrors)
await GameChannel.SendErrorAsync("Hangman Game", $"{msg.Author.Mention} Letter `{guess}` does not exist.\n" + ScrambledWord + "\n" + GetHangman(),
footer: string.Join(" ", Guesses)).ConfigureAwait(false);
else
await End().ConfigureAwait(false);
}
}
}
catch (Exception ex) { _log.Warn(ex); }
}
public string GetHangman() => $@"\_\_\_\_\_\_\_\_\_
| |
| |
{(Errors > 0 ? "😲" : " ")} |
{(Errors > 1 ? "/" : " ")} {(Errors > 2 ? "|" : " ")} {(Errors > 3 ? "\\" : " ")} |
{(Errors > 4 ? "/" : " ")} {(Errors > 5 ? "\\" : " ")} |
/-\";
}
}

View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NadekoBot.Modules.Games.Commands.Hangman
{
public class HangmanObject
{
public string Word { get; set; }
public string ImageUrl { get; set; }
}
}

View File

@ -0,0 +1,66 @@
using Discord;
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.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NadekoBot.Modules.Games
{
public partial class Games
{
[Group]
public class HangmanCommands
{
private static Logger _log { get; }
//channelId, game
public static ConcurrentDictionary<ulong, HangmanGame> HangmanGames { get; } = new ConcurrentDictionary<ulong, HangmanGame>();
static HangmanCommands()
{
_log = LogManager.GetCurrentClassLogger();
}
string typesStr { get; } = "";
public HangmanCommands()
{
typesStr = $"`List of \"{NadekoBot.ModulePrefixes[typeof(Games).Name]}hangman\" term types:`\n" + String.Join(", ", Enum.GetNames(typeof(HangmanTermPool.HangmanTermType)));
}
[NadekoCommand, Usage, Description, Aliases]
public async Task Hangmanlist(IUserMessage imsg)
{
await imsg.Channel.SendConfirmAsync(typesStr);
}
[NadekoCommand, Usage, Description, Aliases]
public async Task Hangman(IUserMessage imsg, HangmanTermPool.HangmanTermType type = HangmanTermPool.HangmanTermType.All)
{
var hm = new HangmanGame(imsg.Channel, type);
if (!HangmanGames.TryAdd(imsg.Channel.Id, hm))
{
await imsg.Channel.SendErrorAsync("Hangman game already running on this channel.").ConfigureAwait(false);
return;
}
hm.OnEnded += (g) =>
{
HangmanGame throwaway;
HangmanGames.TryRemove(g.GameChannel.Id, out throwaway);
};
hm.Start();
await imsg.Channel.SendConfirmAsync("Hangman game started", hm.ScrambledWord + "\n" + hm.GetHangman() + "\n" + hm.ScrambledWord);
}
}
}
}

View File

@ -21,7 +21,7 @@ namespace NadekoBot.Modules.Games
text = text.Trim(); text = text.Trim();
if (string.IsNullOrWhiteSpace(text)) if (string.IsNullOrWhiteSpace(text))
return; return;
await channel.SendMessageAsync(ToLeet(text, level)).ConfigureAwait(false); await channel.SendConfirmAsync("L33t", ToLeet(text, level).SanitizeMentions()).ConfigureAwait(false);
} }

View File

@ -1,6 +1,7 @@
using Discord; using Discord;
using Discord.Commands; using Discord.Commands;
using Discord.WebSocket; using Discord.WebSocket;
using Microsoft.EntityFrameworkCore;
using NadekoBot.Attributes; using NadekoBot.Attributes;
using NadekoBot.Extensions; using NadekoBot.Extensions;
using NadekoBot.Services; using NadekoBot.Services;
@ -16,8 +17,6 @@ using System.Threading.Tasks;
namespace NadekoBot.Modules.Games namespace NadekoBot.Modules.Games
{ {
//todo make currency generation change and cooldown modifyable
//only by bot owner through commands
public partial class Games public partial class Games
{ {
/// <summary> /// <summary>
@ -30,23 +29,22 @@ namespace NadekoBot.Modules.Games
[Group] [Group]
public class PlantPickCommands public class PlantPickCommands
{ {
private Random rng; private static ConcurrentHashSet<ulong> generationChannels { get; } = new ConcurrentHashSet<ulong>();
private ConcurrentHashSet<ulong> generationChannels = new ConcurrentHashSet<ulong>();
//channelid/message //channelid/message
private ConcurrentDictionary<ulong, List<IUserMessage>> plantedFlowers = new ConcurrentDictionary<ulong, List<IUserMessage>>(); private static ConcurrentDictionary<ulong, List<IUserMessage>> plantedFlowers { get; } = new ConcurrentDictionary<ulong, List<IUserMessage>>();
//channelId/last generation //channelId/last generation
private ConcurrentDictionary<ulong, DateTime> lastGenerations = new ConcurrentDictionary<ulong, DateTime>(); private static ConcurrentDictionary<ulong, DateTime> lastGenerations { get; } = new ConcurrentDictionary<ulong, DateTime>();
private float chance; private static ConcurrentHashSet<ulong> usersRecentlyPicked { get; } = new ConcurrentHashSet<ulong>();
private int cooldown;
private Logger _log { get; }
public PlantPickCommands() private static float chance { get; }
private static int cooldown { get; }
private static Logger _log { get; }
static PlantPickCommands()
{ {
_log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
NadekoBot.Client.MessageReceived += PotentialFlowerGeneration; NadekoBot.Client.MessageReceived += PotentialFlowerGeneration;
rng = new NadekoRandom();
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
@ -59,22 +57,23 @@ namespace NadekoBot.Modules.Games
} }
} }
private Task PotentialFlowerGeneration(IMessage imsg) private static async void PotentialFlowerGeneration(IMessage imsg)
{
try
{ {
var msg = imsg as IUserMessage; var msg = imsg as IUserMessage;
if (msg == null || msg.IsAuthor() || msg.Author.IsBot) if (msg == null || msg.IsAuthor() || msg.Author.IsBot)
return Task.CompletedTask; return;
var channel = imsg.Channel as ITextChannel; var channel = imsg.Channel as ITextChannel;
if (channel == null) if (channel == null)
return Task.CompletedTask; return;
if (!generationChannels.Contains(channel.Id)) if (!generationChannels.Contains(channel.Id))
return Task.CompletedTask; return;
var t = Task.Run(async () =>
{
var lastGeneration = lastGenerations.GetOrAdd(channel.Id, DateTime.MinValue); var lastGeneration = lastGenerations.GetOrAdd(channel.Id, DateTime.MinValue);
var rng = new NadekoRandom();
if (DateTime.Now - TimeSpan.FromSeconds(cooldown) < lastGeneration) //recently generated in this channel, don't generate again if (DateTime.Now - TimeSpan.FromSeconds(cooldown) < lastGeneration) //recently generated in this channel, don't generate again
return; return;
@ -84,31 +83,30 @@ namespace NadekoBot.Modules.Games
if (num > 100) if (num > 100)
{ {
lastGenerations.AddOrUpdate(channel.Id, DateTime.Now, (id, old) => DateTime.Now); lastGenerations.AddOrUpdate(channel.Id, DateTime.Now, (id, old) => DateTime.Now);
try
{
var sent = await channel.SendFileAsync( var sent = await channel.SendFileAsync(
GetRandomCurrencyImagePath(), GetRandomCurrencyImagePath(),
$"❗ A random { Gambling.Gambling.CurrencyName } appeared! Pick it up by typing `{NadekoBot.ModulePrefixes[typeof(Games).Name]}pick`") $"❗ A random { Gambling.Gambling.CurrencyName } appeared! Pick it up by typing `{NadekoBot.ModulePrefixes[typeof(Games).Name]}pick`")
.ConfigureAwait(false); .ConfigureAwait(false);
plantedFlowers.AddOrUpdate(channel.Id, new List<IUserMessage>() { sent }, (id, old) => { old.Add(sent); return old; }); plantedFlowers.AddOrUpdate(channel.Id, new List<IUserMessage>() { sent }, (id, old) => { old.Add(sent); return old; });
}
catch { }
} }
});
return Task.CompletedTask;
} }
catch { }
}
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task Pick(IUserMessage imsg) public async Task Pick(IUserMessage imsg)
{ {
var channel = (ITextChannel)imsg.Channel; var channel = (ITextChannel)imsg.Channel;
if (!channel.Guild.GetCurrentUser().GetPermissions(channel).ManageMessages) if (!channel.Guild.GetCurrentUser().GetPermissions(channel).ManageMessages || !usersRecentlyPicked.Add(imsg.Author.Id))
{
await channel.SendMessageAsync("`I need manage channel permissions in order to process this command.`").ConfigureAwait(false);
return; return;
}
try
{
List<IUserMessage> msgs; List<IUserMessage> msgs;
@ -119,12 +117,14 @@ namespace NadekoBot.Modules.Games
await Task.WhenAll(msgs.Select(toDelete => toDelete.DeleteAsync())).ConfigureAwait(false); await Task.WhenAll(msgs.Select(toDelete => toDelete.DeleteAsync())).ConfigureAwait(false);
await CurrencyHandler.AddCurrencyAsync((IGuildUser)imsg.Author, "Picked flower(s).", msgs.Count, false).ConfigureAwait(false); await CurrencyHandler.AddCurrencyAsync((IGuildUser)imsg.Author, "Picked flower(s).", msgs.Count, false).ConfigureAwait(false);
var msg = await channel.SendMessageAsync($"**{imsg.Author.Username}** picked {msgs.Count}{Gambling.Gambling.CurrencySign}!").ConfigureAwait(false); var msg = await channel.SendConfirmAsync($"**{imsg.Author}** picked {msgs.Count}{Gambling.Gambling.CurrencySign}!").ConfigureAwait(false);
var t = Task.Run(async () => msg.DeleteAfter(10);
}
finally
{ {
await Task.Delay(10000).ConfigureAwait(false); await Task.Delay(60000);
try { await msg.DeleteAsync().ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); } usersRecentlyPicked.TryRemove(imsg.Author.Id);
}); }
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -136,7 +136,7 @@ namespace NadekoBot.Modules.Games
var removed = await CurrencyHandler.RemoveCurrencyAsync((IGuildUser)imsg.Author, "Planted a flower.", 1, false).ConfigureAwait(false); var removed = await CurrencyHandler.RemoveCurrencyAsync((IGuildUser)imsg.Author, "Planted a flower.", 1, false).ConfigureAwait(false);
if (!removed) if (!removed)
{ {
await channel.SendMessageAsync($"You don't have any {Gambling.Gambling.CurrencyPluralName}.").ConfigureAwait(false); await channel.SendErrorAsync($"You don't have any {Gambling.Gambling.CurrencyPluralName}.").ConfigureAwait(false);
return; return;
} }
@ -147,7 +147,7 @@ namespace NadekoBot.Modules.Games
var msgToSend = $"Oh how Nice! **{imsg.Author.Username}** planted {(vowelFirst ? "an" : "a")} {Gambling.Gambling.CurrencyName}. Pick it using {NadekoBot.ModulePrefixes[typeof(Games).Name]}pick"; var msgToSend = $"Oh how Nice! **{imsg.Author.Username}** planted {(vowelFirst ? "an" : "a")} {Gambling.Gambling.CurrencyName}. Pick it using {NadekoBot.ModulePrefixes[typeof(Games).Name]}pick";
if (file == null) if (file == null)
{ {
msg = await channel.SendMessageAsync(Gambling.Gambling.CurrencySign).ConfigureAwait(false); msg = await channel.SendConfirmAsync(Gambling.Gambling.CurrencySign).ConfigureAwait(false);
} }
else else
{ {
@ -166,7 +166,7 @@ namespace NadekoBot.Modules.Games
bool enabled; bool enabled;
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
var guildConfig = uow.GuildConfigs.For(channel.Id); var guildConfig = uow.GuildConfigs.For(channel.Id, set => set.Include(gc => gc.GenerateCurrencyChannelIds));
var toAdd = new GCChannelId() { ChannelId = channel.Id }; var toAdd = new GCChannelId() { ChannelId = channel.Id };
if (!guildConfig.GenerateCurrencyChannelIds.Contains(toAdd)) if (!guildConfig.GenerateCurrencyChannelIds.Contains(toAdd))
@ -185,16 +185,19 @@ namespace NadekoBot.Modules.Games
} }
if (enabled) if (enabled)
{ {
await channel.SendMessageAsync("`Currency generation enabled on this channel.`").ConfigureAwait(false); await channel.SendConfirmAsync("Currency generation enabled on this channel.").ConfigureAwait(false);
} }
else else
{ {
await channel.SendMessageAsync($"`Currency generation disabled on this channel.`").ConfigureAwait(false); await channel.SendConfirmAsync("Currency generation disabled on this channel.").ConfigureAwait(false);
} }
} }
private string GetRandomCurrencyImagePath() => private static string GetRandomCurrencyImagePath()
Directory.GetFiles("data/currency_images").OrderBy(s => rng.Next()).FirstOrDefault(); {
var rng = new NadekoRandom();
return Directory.GetFiles("data/currency_images").OrderBy(s => rng.Next()).FirstOrDefault();
}
int GetRandomNumber() int GetRandomNumber()
{ {

View File

@ -1,6 +1,7 @@
using Discord; using Discord;
using Discord.Commands; using Discord.Commands;
using NadekoBot.Attributes; using NadekoBot.Attributes;
using NadekoBot.Extensions;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
@ -44,7 +45,7 @@ namespace NadekoBot.Modules.Games
await poll.StartPoll().ConfigureAwait(false); await poll.StartPoll().ConfigureAwait(false);
} }
else else
await channel.SendMessageAsync("`Poll is already running on this server.`").ConfigureAwait(false); await channel.SendErrorAsync("Poll is already running on this server.").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -91,7 +92,7 @@ namespace NadekoBot.Modules.Games
msgToSend += "\n**Private Message me with the corresponding number of the answer.**"; msgToSend += "\n**Private Message me with the corresponding number of the answer.**";
else else
msgToSend += "\n**Send a Message here with the corresponding number of the answer.**"; msgToSend += "\n**Send a Message here with the corresponding number of the answer.**";
await originalMessage.Channel.SendMessageAsync(msgToSend).ConfigureAwait(false); await originalMessage.Channel.SendConfirmAsync(msgToSend).ConfigureAwait(false);
} }
public async Task StopPoll() public async Task StopPoll()
@ -115,7 +116,7 @@ namespace NadekoBot.Modules.Games
$" has {kvp.Value} votes." + $" has {kvp.Value} votes." +
$"({kvp.Value * 1.0f / totalVotesCast * 100}%)\n"); $"({kvp.Value * 1.0f / totalVotesCast * 100}%)\n");
await originalMessage.Channel.SendMessageAsync($"📄 **Total votes cast**: {totalVotesCast}\n{closeMessage}").ConfigureAwait(false); await originalMessage.Channel.SendConfirmAsync($"📄 **Total votes cast**: {totalVotesCast}\n{closeMessage}").ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -123,24 +124,22 @@ namespace NadekoBot.Modules.Games
} }
} }
private Task Vote(IMessage imsg) private async void Vote(IMessage imsg)
{
try
{ {
// has to be a user message // has to be a user message
var msg = imsg as IUserMessage; var msg = imsg as IUserMessage;
if (msg == null || msg.Author.IsBot) if (msg == null || msg.Author.IsBot)
return Task.CompletedTask; return;
// has to be an integer // has to be an integer
int vote; int vote;
if (!int.TryParse(imsg.Content, out vote)) if (!int.TryParse(imsg.Content, out vote))
return Task.CompletedTask; return;
if (vote < 1 || vote > answers.Length) if (vote < 1 || vote > answers.Length)
return Task.CompletedTask; return;
var t = Task.Run(async () =>
{
try
{
IMessageChannel ch; IMessageChannel ch;
if (isPublic) if (isPublic)
{ {
@ -166,19 +165,16 @@ namespace NadekoBot.Modules.Games
{ {
if (!isPublic) if (!isPublic)
{ {
await ch.SendMessageAsync($"Thanks for voting **{msg.Author.Username}**.").ConfigureAwait(false); await ch.SendConfirmAsync($"Thanks for voting **{msg.Author.Username}**.").ConfigureAwait(false);
} }
else else
{ {
var toDelete = await ch.SendMessageAsync($"{msg.Author.Mention} cast their vote.").ConfigureAwait(false); var toDelete = await ch.SendConfirmAsync($"{msg.Author.Mention} cast their vote.").ConfigureAwait(false);
await Task.Delay(5000); toDelete.DeleteAfter(5);
await toDelete.DeleteAsync().ConfigureAwait(false);
} }
} }
} }
catch { } catch { }
});
return Task.CompletedTask;
} }
} }
} }

View File

@ -21,9 +21,9 @@ namespace NadekoBot.Modules.Games
public class TypingGame public class TypingGame
{ {
public const float WORD_VALUE = 4.5f; public const float WORD_VALUE = 4.5f;
private readonly ITextChannel channel; public ITextChannel Channel { get; }
public string CurrentSentence; public string CurrentSentence { get; private set; }
public bool IsActive; public bool IsActive { get; private set; }
private readonly Stopwatch sw; private readonly Stopwatch sw;
private readonly List<ulong> finishedUserIds; private readonly List<ulong> finishedUserIds;
private Logger _log { get; } private Logger _log { get; }
@ -31,14 +31,12 @@ namespace NadekoBot.Modules.Games
public TypingGame(ITextChannel channel) public TypingGame(ITextChannel channel)
{ {
_log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
this.channel = channel; this.Channel = channel;
IsActive = false; IsActive = false;
sw = new Stopwatch(); sw = new Stopwatch();
finishedUserIds = new List<ulong>(); finishedUserIds = new List<ulong>();
} }
public ITextChannel Channel { get; set; }
public async Task<bool> Stop() public async Task<bool> Stop()
{ {
if (!IsActive) return false; if (!IsActive) return false;
@ -47,7 +45,7 @@ namespace NadekoBot.Modules.Games
IsActive = false; IsActive = false;
sw.Stop(); sw.Stop();
sw.Reset(); sw.Reset();
try { await channel.SendMessageAsync("Typing contest stopped").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); } try { await Channel.SendConfirmAsync("Typing contest stopped.").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
return true; return true;
} }
@ -59,10 +57,10 @@ namespace NadekoBot.Modules.Games
var i = (int)(CurrentSentence.Length / WORD_VALUE * 1.7f); var i = (int)(CurrentSentence.Length / WORD_VALUE * 1.7f);
try try
{ {
await channel.SendMessageAsync($@":clock2: Next contest will last for {i} seconds. Type the bolded text as fast as you can.").ConfigureAwait(false); await Channel.SendConfirmAsync($@":clock2: Next contest will last for {i} seconds. Type the bolded text as fast as you can.").ConfigureAwait(false);
var msg = await channel.SendMessageAsync("Starting new typing contest in **3**...").ConfigureAwait(false); var msg = await Channel.SendMessageAsync("Starting new typing contest in **3**...").ConfigureAwait(false);
await Task.Delay(1000).ConfigureAwait(false); await Task.Delay(1000).ConfigureAwait(false);
try try
{ {
@ -107,18 +105,17 @@ namespace NadekoBot.Modules.Games
NadekoBot.Client.MessageReceived += AnswerReceived; NadekoBot.Client.MessageReceived += AnswerReceived;
} }
private Task AnswerReceived(IMessage imsg) private async void AnswerReceived(IMessage imsg)
{
if (imsg.Author.IsBot)
return Task.CompletedTask;
var msg = imsg as IUserMessage;
if (msg == null)
return Task.CompletedTask;
var t = Task.Run(async () =>
{ {
try try
{ {
if (channel == null || channel.Id != channel.Id) return; if (imsg.Author.IsBot)
return;
var msg = imsg as IUserMessage;
if (msg == null)
return;
if (this.Channel == null || this.Channel.Id != this.Channel.Id) return;
var guess = msg.Content; var guess = msg.Content;
@ -126,17 +123,21 @@ namespace NadekoBot.Modules.Games
var decision = Judge(distance, guess.Length); var decision = Judge(distance, guess.Length);
if (decision && !finishedUserIds.Contains(msg.Author.Id)) if (decision && !finishedUserIds.Contains(msg.Author.Id))
{ {
var wpm = CurrentSentence.Length / WORD_VALUE / sw.Elapsed.Seconds * 60;
finishedUserIds.Add(msg.Author.Id); finishedUserIds.Add(msg.Author.Id);
await channel.SendMessageAsync($"{msg.Author.Mention} finished in **{sw.Elapsed.Seconds}** seconds with { distance } errors, **{ CurrentSentence.Length / WORD_VALUE / sw.Elapsed.Seconds * 60 }** WPM!").ConfigureAwait(false); await Extensions.Extensions.EmbedAsync(this.Channel, (Discord.API.Embed)new EmbedBuilder().WithColor((uint)NadekoBot.OkColor)
.WithTitle((string)$"{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))
.Build()).ConfigureAwait(false);
if (finishedUserIds.Count % 4 == 0) if (finishedUserIds.Count % 4 == 0)
{ {
await channel.SendMessageAsync($":exclamation: `A lot of people finished, here is the text for those still typing:`\n\n**{Format.Sanitize(CurrentSentence.Replace(" ", " \x200B")).SanitizeMentions()}**").ConfigureAwait(false); await Extensions.Extensions.SendConfirmAsync(this.Channel, (string)$":exclamation: A lot of people finished, here is the text for those still typing:\n\n**{Format.Sanitize((string)CurrentSentence.Replace((string)" ", (string)" \x200B")).SanitizeMentions()}**").ConfigureAwait(false);
} }
} }
} }
catch { } catch (Exception ex) { _log.Warn(ex); }
});
return Task.CompletedTask;
} }
private bool Judge(int errors, int textLength) => errors <= textLength / 25; private bool Judge(int errors, int textLength) => errors <= textLength / 25;
@ -172,7 +173,7 @@ namespace NadekoBot.Modules.Games
if (game.IsActive) if (game.IsActive)
{ {
await channel.SendMessageAsync( await channel.SendErrorAsync(
$"Contest already running in " + $"Contest already running in " +
$"{game.Channel.Mention} channel.") $"{game.Channel.Mention} channel.")
.ConfigureAwait(false); .ConfigureAwait(false);
@ -194,7 +195,7 @@ namespace NadekoBot.Modules.Games
await game.Stop().ConfigureAwait(false); await game.Stop().ConfigureAwait(false);
return; return;
} }
await channel.SendMessageAsync("No contest to stop on this channel.").ConfigureAwait(false); await channel.SendErrorAsync("No contest to stop on this channel.").ConfigureAwait(false);
} }
@ -213,7 +214,7 @@ namespace NadekoBot.Modules.Games
File.WriteAllText(typingArticlesPath, JsonConvert.SerializeObject(TypingArticles)); File.WriteAllText(typingArticlesPath, JsonConvert.SerializeObject(TypingArticles));
await channel.SendMessageAsync("Added new article for typing game.").ConfigureAwait(false); await channel.SendConfirmAsync("Added new article for typing game.").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -229,11 +230,11 @@ namespace NadekoBot.Modules.Games
if (!articles.Any()) if (!articles.Any())
{ {
await channel.SendMessageAsync($"{imsg.Author.Mention} `No articles found on that page.`").ConfigureAwait(false); await channel.SendErrorAsync($"{imsg.Author.Mention} `No articles found on that page.`").ConfigureAwait(false);
return; return;
} }
var i = (page - 1) * 15; var i = (page - 1) * 15;
await channel.SendMessageAsync(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); .ConfigureAwait(false);
} }
@ -253,7 +254,7 @@ namespace NadekoBot.Modules.Games
File.WriteAllText(typingArticlesPath, JsonConvert.SerializeObject(TypingArticles)); File.WriteAllText(typingArticlesPath, JsonConvert.SerializeObject(TypingArticles));
await channel.SendMessageAsync($"`Removed typing article:` #{index + 1} - {removed.Text.TrimTo(50)}") await channel.SendConfirmAsync($"`Removed typing article:` #{index + 1} - {removed.Text.TrimTo(50)}")
.ConfigureAwait(false); .ConfigureAwait(false);
} }
} }

View File

@ -22,7 +22,7 @@ namespace NadekoBot.Modules.Games.Trivia
private int QuestionDurationMiliseconds { get; } = 30000; private int QuestionDurationMiliseconds { get; } = 30000;
private int HintTimeoutMiliseconds { get; } = 6000; private int HintTimeoutMiliseconds { get; } = 6000;
public bool ShowHints { get; set; } = true; public bool ShowHints { get; } = true;
private CancellationTokenSource triviaCancelSource { get; set; } private CancellationTokenSource triviaCancelSource { get; set; }
public TriviaQuestion CurrentQuestion { get; private set; } public TriviaQuestion CurrentQuestion { get; private set; }
@ -35,52 +35,71 @@ namespace NadekoBot.Modules.Games.Trivia
public int WinRequirement { get; } = 10; public int WinRequirement { get; } = 10;
public TriviaGame(IGuild guild, ITextChannel channel, bool showHints, int winReq = 10) public TriviaGame(IGuild guild, ITextChannel channel, bool showHints, int winReq)
{ {
_log = LogManager.GetCurrentClassLogger(); this._log = LogManager.GetCurrentClassLogger();
ShowHints = showHints;
this.ShowHints = showHints;
this.guild = guild; this.guild = guild;
this.channel = channel; this.channel = channel;
WinRequirement = winReq; this.WinRequirement = winReq;
Task.Run(async () => { try { await StartGame().ConfigureAwait(false); } catch { } });
} }
private async Task StartGame() public async Task StartGame()
{ {
while (!ShouldStopGame) while (!ShouldStopGame)
{ {
// reset the cancellation source // reset the cancellation source
triviaCancelSource = new CancellationTokenSource(); triviaCancelSource = new CancellationTokenSource();
var token = triviaCancelSource.Token;
// load question // load question
CurrentQuestion = TriviaQuestionPool.Instance.GetRandomQuestion(oldQuestions); CurrentQuestion = TriviaQuestionPool.Instance.GetRandomQuestion(oldQuestions);
if (CurrentQuestion == null) if (CurrentQuestion == null)
{ {
try { await channel.SendMessageAsync($":exclamation: Failed loading a trivia question").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); } await channel.SendErrorAsync("Trivia Game", "Failed loading a question.").ConfigureAwait(false);
await End().ConfigureAwait(false);
return; 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
//sendquestion
try { await channel.SendMessageAsync($":question: **{CurrentQuestion.Question}**").ConfigureAwait(false); } 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));
questionMessage = await channel.EmbedAsync(questionEmbed.Build()).ConfigureAwait(false);
}
catch (HttpException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound || ex.StatusCode == System.Net.HttpStatusCode.Forbidden) catch (HttpException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound || ex.StatusCode == System.Net.HttpStatusCode.Forbidden)
{ {
break; return;
}
catch (Exception ex)
{
_log.Warn(ex);
await Task.Delay(2000).ConfigureAwait(false);
continue;
} }
catch (Exception ex) { _log.Warn(ex); }
//receive messages //receive messages
try
{
NadekoBot.Client.MessageReceived += PotentialGuess; NadekoBot.Client.MessageReceived += PotentialGuess;
//allow people to guess //allow people to guess
GameActive = true; GameActive = true;
try try
{ {
//hint //hint
await Task.Delay(HintTimeoutMiliseconds, token).ConfigureAwait(false); await Task.Delay(HintTimeoutMiliseconds, triviaCancelSource.Token).ConfigureAwait(false);
if (ShowHints) if (ShowHints)
try { await channel.SendMessageAsync($":exclamation:**Hint:** {CurrentQuestion.GetHint()}").ConfigureAwait(false); } try
{
await questionMessage.ModifyAsync(m => m.Embed = questionEmbed.WithFooter(efb => efb.WithText(CurrentQuestion.GetHint())).Build())
.ConfigureAwait(false);
}
catch (HttpException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound || ex.StatusCode == System.Net.HttpStatusCode.Forbidden) catch (HttpException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound || ex.StatusCode == System.Net.HttpStatusCode.Forbidden)
{ {
break; break;
@ -88,53 +107,57 @@ namespace NadekoBot.Modules.Games.Trivia
catch (Exception ex) { _log.Warn(ex); } catch (Exception ex) { _log.Warn(ex); }
//timeout //timeout
await Task.Delay(QuestionDurationMiliseconds - HintTimeoutMiliseconds, token).ConfigureAwait(false); await Task.Delay(QuestionDurationMiliseconds - HintTimeoutMiliseconds, triviaCancelSource.Token).ConfigureAwait(false);
} }
catch (TaskCanceledException) { } //means someone guessed the answer catch (TaskCanceledException) { } //means someone guessed the answer
}
finally
{
GameActive = false; GameActive = false;
if (!triviaCancelSource.IsCancellationRequested)
try { await channel.SendMessageAsync($":clock2: :question: **Time's up!** The correct answer was **{CurrentQuestion.Answer}**").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
NadekoBot.Client.MessageReceived -= PotentialGuess; NadekoBot.Client.MessageReceived -= PotentialGuess;
// load next question if game is still running }
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); }
await Task.Delay(2000).ConfigureAwait(false); await Task.Delay(2000).ConfigureAwait(false);
} }
try { NadekoBot.Client.MessageReceived -= PotentialGuess; } catch { }
GameActive = false;
await End().ConfigureAwait(false);
} }
private async Task End() public async Task EnsureStopped()
{ {
ShouldStopGame = true; ShouldStopGame = true;
TriviaGame throwaway;
Games.TriviaCommands.RunningTrivias.TryRemove(channel.Guild.Id, out throwaway); await channel.EmbedAsync(new EmbedBuilder().WithOkColor()
try { await channel.SendMessageAsync("**Trivia game ended**\n" + GetLeaderboard()).ConfigureAwait(false); } catch { } .WithAuthor(eab => eab.WithName("Trivia Game Ended"))
.WithTitle("Final Results")
.WithDescription(GetLeaderboard())
.Build()).ConfigureAwait(false);
} }
public async Task StopGame() public async Task StopGame()
{ {
if (!ShouldStopGame) var old = ShouldStopGame;
try { await channel.SendMessageAsync(":exclamation: Trivia will stop after this question.").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
ShouldStopGame = true; ShouldStopGame = true;
if (!old)
try { await channel.SendConfirmAsync("Trivia Game", "Stopping after this question.").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
} }
private Task PotentialGuess(IMessage imsg) private async void PotentialGuess(IMessage imsg)
{
if (imsg.Author.IsBot)
return Task.CompletedTask;
var umsg = imsg as IUserMessage;
if (umsg == null)
return Task.CompletedTask;
var t = Task.Run(async () =>
{ {
try try
{ {
if (!(umsg.Channel is IGuildChannel && umsg.Channel is ITextChannel)) return; if (imsg.Author.IsBot)
if ((umsg.Channel as ITextChannel).Guild != guild) return; return;
if (umsg.Author.Id == NadekoBot.Client.GetCurrentUser().Id) return;
var guildUser = umsg.Author as IGuildUser; var umsg = imsg as IUserMessage;
if (umsg == null)
return;
var textChannel = umsg.Channel as ITextChannel;
if (textChannel == null || textChannel.Guild != guild)
return;
var guildUser = (IGuildUser)umsg.Author;
var guess = false; var guess = false;
await _guessLock.WaitAsync().ConfigureAwait(false); await _guessLock.WaitAsync().ConfigureAwait(false);
@ -149,23 +172,26 @@ namespace NadekoBot.Modules.Games.Trivia
finally { _guessLock.Release(); } finally { _guessLock.Release(); }
if (!guess) return; if (!guess) return;
triviaCancelSource.Cancel(); triviaCancelSource.Cancel();
try { await channel.SendMessageAsync($"☑️ {guildUser.Mention} guessed it! The answer was: **{CurrentQuestion.Answer}**").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
if (Users[guildUser] != WinRequirement) return;
if (Users[guildUser] == WinRequirement)
{
ShouldStopGame = true; ShouldStopGame = true;
await channel.SendMessageAsync($":exclamation: We have a winner! It's {guildUser.Mention}.").ConfigureAwait(false); await channel.SendConfirmAsync("Trivia Game", $"{guildUser.Mention} guessed it and WON the game! The answer was: **{CurrentQuestion.Answer}**").ConfigureAwait(false);
return;
}
await channel.SendConfirmAsync("Trivia Game", $"{guildUser.Mention} guessed it! The answer was: **{CurrentQuestion.Answer}**").ConfigureAwait(false);
} }
catch (Exception ex) { _log.Warn(ex); } catch (Exception ex) { _log.Warn(ex); }
});
return Task.CompletedTask;
} }
public string GetLeaderboard() public string GetLeaderboard()
{ {
if (Users.Count == 0) if (Users.Count == 0)
return ""; return "No results.";
var sb = new StringBuilder(); var sb = new StringBuilder();
sb.Append("**Leaderboard:**\n-----------\n");
foreach (var kvp in Users.OrderByDescending(kvp => kvp.Value)) foreach (var kvp in Users.OrderByDescending(kvp => kvp.Value))
{ {

View File

@ -18,9 +18,9 @@ namespace NadekoBot.Modules.Games.Trivia
}; };
public static int maxStringLength = 22; public static int maxStringLength = 22;
public string Category; public string Category { get; set; }
public string Question; public string Question { get; set; }
public string Answer; public string Answer { get; set; }
public TriviaQuestion(string q, string a, string c) public TriviaQuestion(string q, string a, string c)
{ {
@ -79,12 +79,9 @@ namespace NadekoBot.Modules.Games.Trivia
return str; return str;
} }
public override string ToString() =>
"Question: **" + this.Question + "?**";
private static string Scramble(string word) private static string Scramble(string word)
{ {
var letters = word.ToArray(); var letters = word.ToCharArray();
var count = 0; var count = 0;
for (var i = 0; i < letters.Length; i++) for (var i = 0; i < letters.Length; i++)
{ {
@ -101,7 +98,7 @@ namespace NadekoBot.Modules.Games.Trivia
if (letters[i] != ' ') if (letters[i] != ' ')
letters[i] = '_'; letters[i] = '_';
} }
return "`" + string.Join(" ", letters) + "`"; return string.Join(" \x200B", new string(letters).Replace(" ", " \x200B").AsEnumerable());
} }
} }
} }

View File

@ -1,5 +1,6 @@
using NadekoBot.Extensions; using NadekoBot.Extensions;
using NadekoBot.Services; using NadekoBot.Services;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
@ -11,36 +12,31 @@ namespace NadekoBot.Modules.Games.Trivia
{ {
public class TriviaQuestionPool public class TriviaQuestionPool
{ {
public static TriviaQuestionPool Instance { get; } = new TriviaQuestionPool(); private static TriviaQuestionPool _instance;
public ConcurrentHashSet<TriviaQuestion> pool = new ConcurrentHashSet<TriviaQuestion>(); public static TriviaQuestionPool Instance { get; } = _instance ?? (_instance = new TriviaQuestionPool());
private const string questionsFile = "data/trivia_questions.json";
private Random rng { get; } = new NadekoRandom(); private Random rng { get; } = new NadekoRandom();
private TriviaQuestion[] pool { get; }
static TriviaQuestionPool() { } static TriviaQuestionPool() { }
private TriviaQuestionPool() private TriviaQuestionPool()
{ {
Reload(); pool = JsonConvert.DeserializeObject<TriviaQuestion[]>(File.ReadAllText(questionsFile));
} }
public TriviaQuestion GetRandomQuestion(IEnumerable<TriviaQuestion> exclude) public TriviaQuestion GetRandomQuestion(HashSet<TriviaQuestion> exclude)
{ {
var list = pool.Except(exclude).ToList(); if (pool.Length == 0)
var rand = rng.Next(0, list.Count); return null;
return list[rand];
}
public void Reload() TriviaQuestion randomQuestion;
{ while (exclude.Contains(randomQuestion = pool[rng.Next(0, pool.Length)])) ;
var arr = JArray.Parse(File.ReadAllText("data/questions.json"));
foreach (var item in arr) return randomQuestion;
{
var tq = new TriviaQuestion(item["Question"].ToString().SanitizeMentions(), item["Answer"].ToString().SanitizeMentions(), item["Category"]?.ToString());
pool.Add(tq);
}
var r = new NadekoRandom();
pool = new ConcurrentHashSet<TriviaQuestion>(pool.OrderBy(x => r.Next()));
} }
} }
} }

View File

@ -1,6 +1,7 @@
using Discord; using Discord;
using Discord.Commands; using Discord.Commands;
using NadekoBot.Attributes; using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Modules.Games.Trivia; using NadekoBot.Modules.Games.Trivia;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
@ -19,29 +20,33 @@ namespace NadekoBot.Modules.Games
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task Trivia(IUserMessage umsg, params string[] args) public Task Trivia(IUserMessage umsg, [Remainder] string additionalArgs = "")
=> Trivia(umsg, 10, additionalArgs);
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Trivia(IUserMessage umsg, int winReq = 10, [Remainder] string additionalArgs = "")
{ {
var channel = (ITextChannel)umsg.Channel; var channel = (ITextChannel)umsg.Channel;
TriviaGame trivia; var showHints = !additionalArgs.Contains("nohint");
if (!RunningTrivias.TryGetValue(channel.Guild.Id, out trivia))
TriviaGame trivia = new TriviaGame(channel.Guild, channel, showHints, winReq);
if (RunningTrivias.TryAdd(channel.Guild.Id, trivia))
{ {
var showHints = !args.Contains("nohint"); try
var number = args.Select(s =>
{ {
int num; await trivia.StartGame().ConfigureAwait(false);
return new Tuple<bool, int>(int.TryParse(s, out num), num);
}).Where(t => t.Item1).Select(t => t.Item2).FirstOrDefault();
if (number < 0)
return;
var triviaGame = new TriviaGame(channel.Guild, (ITextChannel)umsg.Channel, showHints, number == 0 ? 10 : number);
if (RunningTrivias.TryAdd(channel.Guild.Id, triviaGame))
await channel.SendMessageAsync($"**Trivia game started! {triviaGame.WinRequirement} points needed to win.**").ConfigureAwait(false);
else
await triviaGame.StopGame().ConfigureAwait(false);
} }
else finally
await channel.SendMessageAsync("Trivia game is already running on this server.\n" + trivia.CurrentQuestion).ConfigureAwait(false); {
RunningTrivias.TryRemove(channel.Guild.Id, out trivia);
await trivia.EnsureStopped().ConfigureAwait(false);
}
return;
}
await channel.SendErrorAsync("Trivia game is already running on this server.\n" + trivia.CurrentQuestion).ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -52,9 +57,12 @@ namespace NadekoBot.Modules.Games
TriviaGame trivia; TriviaGame trivia;
if (RunningTrivias.TryGetValue(channel.Guild.Id, out trivia)) if (RunningTrivias.TryGetValue(channel.Guild.Id, out trivia))
await channel.SendMessageAsync(trivia.GetLeaderboard()).ConfigureAwait(false); {
else await channel.SendConfirmAsync("Leaderboard", trivia.GetLeaderboard()).ConfigureAwait(false);
await channel.SendMessageAsync("No trivia is running on this server.").ConfigureAwait(false); return;
}
await channel.SendErrorAsync("No trivia is running on this server.").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -64,12 +72,13 @@ namespace NadekoBot.Modules.Games
var channel = (ITextChannel)umsg.Channel; var channel = (ITextChannel)umsg.Channel;
TriviaGame trivia; TriviaGame trivia;
if (RunningTrivias.TryRemove(channel.Guild.Id, out trivia)) if (RunningTrivias.TryGetValue(channel.Guild.Id, out trivia))
{ {
await trivia.StopGame().ConfigureAwait(false); await trivia.StopGame().ConfigureAwait(false);
return;
} }
else
await channel.SendMessageAsync("No trivia is running on this server.").ConfigureAwait(false); await channel.SendErrorAsync("No trivia is running on this server.").ConfigureAwait(false);
} }
} }
} }

View File

@ -21,7 +21,7 @@ namespace NadekoBot.Modules.Games
} }
} }
} }
public Games(ILocalization loc, CommandService cmds, ShardedDiscordClient client) : base(loc, cmds, client) public Games() : base()
{ {
} }
@ -36,7 +36,7 @@ namespace NadekoBot.Modules.Games
if (listArr.Count() < 2) if (listArr.Count() < 2)
return; return;
var rng = new NadekoRandom(); var rng = new NadekoRandom();
await channel.SendMessageAsync(listArr[rng.Next(0, listArr.Length)]).ConfigureAwait(false); await channel.SendConfirmAsync("🤔", listArr[rng.Next(0, listArr.Length)]).ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -48,8 +48,11 @@ namespace NadekoBot.Modules.Games
if (string.IsNullOrWhiteSpace(question)) if (string.IsNullOrWhiteSpace(question))
return; return;
var rng = new NadekoRandom(); var rng = new NadekoRandom();
await channel.SendMessageAsync($@"❓ `Question` __**{question}**__
🎱 `8Ball Answers` __**{_8BallResponses.Shuffle().FirstOrDefault()}**__").ConfigureAwait(false); await channel.EmbedAsync(new EmbedBuilder().WithOkColor()
.AddField(efb => efb.WithName("❓ Question").WithValue(question).WithIsInline(false))
.AddField(efb => efb.WithName("🎱 8Ball").WithValue(_8BallResponses.Shuffle().FirstOrDefault()).WithIsInline(false))
.Build());
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -99,7 +102,7 @@ namespace NadekoBot.Modules.Games
else else
msg = $"{umsg.Author.Mention} won! {GetRPSPick(pick)} beats {GetRPSPick(nadekoPick)}"; msg = $"{umsg.Author.Mention} won! {GetRPSPick(pick)} beats {GetRPSPick(nadekoPick)}";
await channel.SendMessageAsync(msg).ConfigureAwait(false); await channel.SendConfirmAsync(msg).ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -108,7 +111,7 @@ namespace NadekoBot.Modules.Games
{ {
var channel = (ITextChannel)umsg.Channel; var channel = (ITextChannel)umsg.Channel;
await channel.SendMessageAsync( await channel.SendConfirmAsync(
$@"I'd just like to interject for moment. What you're refering to as {loonix}, is in fact, {guhnoo}/{loonix}, or as I've recently taken to calling it, {guhnoo} plus {loonix}. {loonix} is not an operating system unto itself, but rather another free component of a fully functioning {guhnoo} system made useful by the {guhnoo} corelibs, shell utilities and vital system components comprising a full OS as defined by POSIX. $@"I'd just like to interject for moment. What you're refering to as {loonix}, is in fact, {guhnoo}/{loonix}, or as I've recently taken to calling it, {guhnoo} plus {loonix}. {loonix} is not an operating system unto itself, but rather another free component of a fully functioning {guhnoo} system made useful by the {guhnoo} corelibs, shell utilities and vital system components comprising a full OS as defined by POSIX.
Many computer users run a modified version of the {guhnoo} system every day, without realizing it. Through a peculiar turn of events, the version of {guhnoo} which is widely used today is often called {loonix}, and many of its users are not aware that it is basically the {guhnoo} system, developed by the {guhnoo} Project. Many computer users run a modified version of the {guhnoo} system every day, without realizing it. Through a peculiar turn of events, the version of {guhnoo} which is widely used today is often called {loonix}, and many of its users are not aware that it is basically the {guhnoo} system, developed by the {guhnoo} Project.

View File

@ -30,7 +30,7 @@ namespace NadekoBot.Modules.Help
} }
} }
public Help(ILocalization loc, CommandService cmds, ShardedDiscordClient client) : base(loc, cmds, client) public Help() : base()
{ {
} }
@ -38,8 +38,10 @@ namespace NadekoBot.Modules.Help
public async Task Modules(IUserMessage umsg) public async Task Modules(IUserMessage umsg)
{ {
await umsg.Channel.SendMessageAsync("📜 **List of modules:** ```css\n• " + string.Join("\n• ", _commands.Modules.Select(m => m.Name)) + $"\n``` **Type** `-commands module_name` **to get a list of commands in that module.** ***e.g.*** `-commands games`") var embed = new EmbedBuilder().WithOkColor().WithFooter(efb => efb.WithText($" Type `-cmds ModuleName` to get a list of commands in that module. eg `-cmds games`"))
.ConfigureAwait(false); .WithTitle("📜 List Of Modules").WithDescription("\n• " + string.Join("\n• ", NadekoBot.CommandService.Modules.Select(m => m.Name).OrderBy(s=>s)))
.Build();
await umsg.Channel.EmbedAsync(embed).ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -50,7 +52,7 @@ namespace NadekoBot.Modules.Help
module = module?.Trim().ToUpperInvariant(); module = module?.Trim().ToUpperInvariant();
if (string.IsNullOrWhiteSpace(module)) if (string.IsNullOrWhiteSpace(module))
return; return;
var cmds = _commands.Commands.Where(c => c.Module.Name.ToUpperInvariant().StartsWith(module)) var cmds = NadekoBot.CommandService.Commands.Where(c => c.Module.Name.ToUpperInvariant().StartsWith(module))
.OrderBy(c => c.Text) .OrderBy(c => c.Text)
.Distinct(new CommandTextEqualityComparer()) .Distinct(new CommandTextEqualityComparer())
.AsEnumerable(); .AsEnumerable();
@ -58,7 +60,7 @@ namespace NadekoBot.Modules.Help
var cmdsArray = cmds as Command[] ?? cmds.ToArray(); var cmdsArray = cmds as Command[] ?? cmds.ToArray();
if (!cmdsArray.Any()) if (!cmdsArray.Any())
{ {
await channel.SendMessageAsync("🚫 **That module does not exist.**").ConfigureAwait(false); await channel.SendErrorAsync("That module does not exist.").ConfigureAwait(false);
return; return;
} }
if (module != "customreactions" && module != "conversations") if (module != "customreactions" && module != "conversations")
@ -69,7 +71,7 @@ namespace NadekoBot.Modules.Help
{ {
await channel.SendMessageAsync("📃 **List Of Commands:**\n• " + string.Join("\n• ", cmdsArray.Select(c => $"{c.Text}"))); await channel.SendMessageAsync("📃 **List Of Commands:**\n• " + string.Join("\n• ", cmdsArray.Select(c => $"{c.Text}")));
} }
await channel.SendMessageAsync($" **Type** `\"{NadekoBot.ModulePrefixes[typeof(Help).Name]}h CommandName\"` **to see the help for that specified command.** ***e.g.*** `-h >8ball`").ConfigureAwait(false); 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);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -84,20 +86,22 @@ namespace NadekoBot.Modules.Help
await ch.SendMessageAsync(HelpString).ConfigureAwait(false); await ch.SendMessageAsync(HelpString).ConfigureAwait(false);
return; return;
} }
var com = _commands.Commands.FirstOrDefault(c => c.Text.ToLowerInvariant() == comToFind || c.Aliases.Select(a=>a.ToLowerInvariant()).Contains(comToFind)); var com = NadekoBot.CommandService.Commands.FirstOrDefault(c => c.Text.ToLowerInvariant() == comToFind || c.Aliases.Select(a=>a.ToLowerInvariant()).Contains(comToFind));
if (com == null) if (com == null)
{ {
await channel.SendMessageAsync("🔍 **I can't find that command.**"); await channel.SendErrorAsync("I can't find that command. Please check the **command** and **command prefix** before trying again.");
return; return;
} }
var str = $"**__Help for:__ `{com.Text}`**"; var str = $"**`{com.Text}`**";
var alias = com.Aliases.Skip(1).FirstOrDefault(); var alias = com.Aliases.Skip(1).FirstOrDefault();
if (alias != null) if (alias != null)
str += $" / `{alias}`"; str += $" **/ `{alias}`**";
if (com != null) var embed = new EmbedBuilder()
await channel.SendMessageAsync(str + $@"{Environment.NewLine}**Desc:** {string.Format(com.Summary, com.Module.Prefix)} {GetCommandRequirements(com)} .AddField(fb => fb.WithIndex(1).WithName(str).WithValue($"{ string.Format(com.Summary, com.Module.Prefix)} { GetCommandRequirements(com)}").WithIsInline(true))
**Usage:** {string.Format(com.Remarks, com.Module.Prefix)}").ConfigureAwait(false); .AddField(fb => fb.WithIndex(2).WithName("**Usage**").WithValue($"{string.Format(com.Remarks, com.Module.Prefix)}").WithIsInline(false))
.WithOkColor();
await channel.EmbedAsync(embed.Build()).ConfigureAwait(false);
} }
private string GetCommandRequirements(Command cmd) private string GetCommandRequirements(Command cmd)
@ -126,7 +130,7 @@ namespace NadekoBot.Modules.Help
helpstr.AppendLine(string.Join("\n", NadekoBot.CommandService.Modules.Where(m => m.Name.ToLowerInvariant() != "help").OrderBy(m => m.Name).Prepend(NadekoBot.CommandService.Modules.FirstOrDefault(m=>m.Name.ToLowerInvariant()=="help")).Select(m => $"- [{m.Name}](#{m.Name.ToLowerInvariant()})"))); helpstr.AppendLine(string.Join("\n", NadekoBot.CommandService.Modules.Where(m => m.Name.ToLowerInvariant() != "help").OrderBy(m => m.Name).Prepend(NadekoBot.CommandService.Modules.FirstOrDefault(m=>m.Name.ToLowerInvariant()=="help")).Select(m => $"- [{m.Name}](#{m.Name.ToLowerInvariant()})")));
helpstr.AppendLine(); helpstr.AppendLine();
string lastModule = null; string lastModule = null;
foreach (var com in _commands.Commands.OrderBy(com=>com.Module.Name).GroupBy(c=>c.Text).Select(g=>g.First())) foreach (var com in NadekoBot.CommandService.Commands.OrderBy(com=>com.Module.Name).GroupBy(c=>c.Text).Select(g=>g.First()))
{ {
if (com.Module.Name != lastModule) if (com.Module.Name != lastModule)
{ {
@ -154,7 +158,7 @@ namespace NadekoBot.Modules.Help
{ {
var channel = (ITextChannel)umsg.Channel; var channel = (ITextChannel)umsg.Channel;
await channel.SendMessageAsync( await channel.SendConfirmAsync(
@"**LIST OF COMMANDS**: <http://nadekobot.readthedocs.io/en/latest/Commands%20List/> @"**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); **Hosting Guides and docs can be found here**: <http://nadekobot.readthedocs.io/en/latest/>").ConfigureAwait(false);
} }
@ -165,7 +169,7 @@ namespace NadekoBot.Modules.Help
{ {
var channel = (ITextChannel)umsg.Channel; var channel = (ITextChannel)umsg.Channel;
await channel.SendMessageAsync( await channel.SendConfirmAsync(
$@"You can support the NadekoBot project on patreon. <https://patreon.com/nadekobot> or $@"You can support the NadekoBot project on patreon. <https://patreon.com/nadekobot> or
You can send donations to `nadekodiscordbot@gmail.com` You can send donations to `nadekodiscordbot@gmail.com`
Don't forget to leave your discord name or id in the message. Don't forget to leave your discord name or id in the message.

View File

@ -30,6 +30,26 @@ namespace NadekoBot.Modules.Music.Classes
{ {
private IAudioClient audioClient { get; set; } private IAudioClient audioClient { get; set; }
/// <summary>
/// Player will prioritize different queuer name
/// over the song position in the playlist
/// </summary>
public bool FairPlay { get; set; } = false;
/// <summary>
/// Song will stop playing after this amount of time.
/// To prevent people queueing radio or looped songs
/// while other people want to listen to other songs too.
/// </summary>
public uint MaxPlaytimeSeconds { get; set; } = 0;
public TimeSpan TotalPlaytime => 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>(); private readonly List<Song> playlist = new List<Song>();
public IReadOnlyCollection<Song> Playlist => playlist; public IReadOnlyCollection<Song> Playlist => playlist;
@ -41,8 +61,9 @@ namespace NadekoBot.Modules.Music.Classes
public float Volume { get; private set; } public float Volume { get; private set; }
public event EventHandler<Song> OnCompleted = delegate { }; public event Action<MusicPlayer, Song> OnCompleted = delegate { };
public event EventHandler<Song> OnStarted = delegate { }; public event Action<MusicPlayer, Song> OnStarted = delegate { };
public event Action<bool> OnPauseChanged = delegate { };
public IVoiceChannel PlaybackVoiceChannel { get; private set; } public IVoiceChannel PlaybackVoiceChannel { get; private set; }
@ -52,7 +73,9 @@ namespace NadekoBot.Modules.Music.Classes
public bool Autoplay { get; set; } = false; public bool Autoplay { get; set; } = false;
public uint MaxQueueSize { get; set; } = 0; public uint MaxQueueSize { get; set; } = 0;
private ConcurrentQueue<Action> actionQueue { get; set; } = new ConcurrentQueue<Action>(); private ConcurrentQueue<Action> actionQueue { get; } = new ConcurrentQueue<Action>();
public string PrettyVolume => $"🔉 {(int)(Volume * 100)}%";
public MusicPlayer(IVoiceChannel startingVoiceChannel, float? defaultVolume) public MusicPlayer(IVoiceChannel startingVoiceChannel, float? defaultVolume)
{ {
@ -106,11 +129,13 @@ namespace NadekoBot.Modules.Music.Classes
} }
CurrentSong = GetNextSong(); CurrentSong = GetNextSong();
RemoveSongAt(0);
if (CurrentSong == null) if (CurrentSong == null)
continue; continue;
var index = playlist.IndexOf(CurrentSong);
if (index != -1)
RemoveSongAt(index);
OnStarted(this, CurrentSong); OnStarted(this, CurrentSong);
await CurrentSong.Play(audioClient, cancelToken); await CurrentSong.Play(audioClient, cancelToken);
@ -129,6 +154,7 @@ namespace NadekoBot.Modules.Music.Classes
{ {
Console.WriteLine("Music thread almost crashed."); Console.WriteLine("Music thread almost crashed.");
Console.WriteLine(ex); Console.WriteLine(ex);
await Task.Delay(3000).ConfigureAwait(false);
} }
finally finally
{ {
@ -168,7 +194,7 @@ namespace NadekoBot.Modules.Music.Classes
}); });
} }
public void TogglePause() => Paused = !Paused; public void TogglePause() => OnPauseChanged(Paused = !Paused);
public int SetVolume(int volume) public int SetVolume(int volume)
{ {
@ -181,8 +207,26 @@ namespace NadekoBot.Modules.Music.Classes
return volume; return volume;
} }
private Song GetNextSong() => private Song GetNextSong()
playlist.FirstOrDefault(); {
if (!FairPlay)
{
return playlist.FirstOrDefault();
}
var song = playlist.FirstOrDefault(c => !recentlyPlayedUsers.Contains(c.QueuerName))
?? playlist.FirstOrDefault();
if (song == null)
return null;
if (recentlyPlayedUsers.Contains(song.QueuerName))
{
recentlyPlayedUsers.Clear();
}
recentlyPlayedUsers.Add(song.QueuerName);
return song;
}
public void Shuffle() public void Shuffle()
{ {
@ -249,7 +293,7 @@ namespace NadekoBot.Modules.Music.Classes
{ {
var curSong = CurrentSong; var curSong = CurrentSong;
var toUpdate = playlist.Where(s => s.SongInfo.ProviderType == MusicType.Normal && var toUpdate = playlist.Where(s => s.SongInfo.ProviderType == MusicType.Normal &&
s.TotalLength == TimeSpan.Zero); s.TotalTime == TimeSpan.Zero);
if (curSong != null) if (curSong != null)
toUpdate = toUpdate.Append(curSong); toUpdate = toUpdate.Append(curSong);
var ids = toUpdate.Select(s => s.SongInfo.Query.Substring(s.SongInfo.Query.LastIndexOf("?v=") + 3)) var ids = toUpdate.Select(s => s.SongInfo.Query.Substring(s.SongInfo.Query.LastIndexOf("?v=") + 3))
@ -263,11 +307,12 @@ namespace NadekoBot.Modules.Music.Classes
{ {
if (s.SongInfo.Query.EndsWith(kvp.Key)) if (s.SongInfo.Query.EndsWith(kvp.Key))
{ {
s.TotalLength = kvp.Value; s.TotalTime = kvp.Value;
return; return;
} }
} }
}); });
} }
public void Destroy() public void Destroy()

View File

@ -0,0 +1,15 @@
using Discord;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NadekoBot.Modules.Music.Classes
{
public static class MusicExtensions
{
public static EmbedAuthorBuilder WithMusicIcon(this EmbedAuthorBuilder eab) =>
eab.WithIconUrl("http://i.imgur.com/nhKS3PT.png");
}
}

View File

@ -11,6 +11,7 @@ using System.Text.RegularExpressions;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using VideoLibrary; using VideoLibrary;
using System.Net;
namespace NadekoBot.Modules.Music.Classes namespace NadekoBot.Modules.Music.Classes
{ {
@ -18,35 +19,20 @@ namespace NadekoBot.Modules.Music.Classes
{ {
public string Provider { get; set; } public string Provider { get; set; }
public MusicType ProviderType { get; set; } public MusicType ProviderType { get; set; }
/// <summary>
/// Will be set only if the providertype is normal
/// </summary>
public string Query { get; set; } public string Query { get; set; }
public string Title { get; set; } public string Title { get; set; }
public string Uri { get; set; } public string Uri { get; set; }
public string AlbumArt { get; set; }
} }
public class Song public class Song
{ {
public StreamState State { get; set; }
public string PrettyName =>
$"**【 {SongInfo.Title.TrimTo(55)} 】**`{(SongInfo.Provider ?? "-")}` `by {QueuerName}`";
public SongInfo SongInfo { get; } public SongInfo SongInfo { get; }
public MusicPlayer MusicPlayer { get; set; }
public string QueuerName { get; set; } public string QueuerName { get; set; }
public MusicPlayer MusicPlayer { get; set; } public TimeSpan TotalTime { get; set; } = TimeSpan.Zero;
public TimeSpan CurrentTime => TimeSpan.FromSeconds(bytesSent / frameBytes / (1000 / milliseconds));
public string PrettyCurrentTime()
{
var time = TimeSpan.FromSeconds(bytesSent / 3840 / 50);
var str = $"【{(int)time.TotalMinutes}m {time.Seconds}s】**/** ";
if (TotalLength == TimeSpan.Zero)
str += "**?**";
else if (TotalLength == TimeSpan.MaxValue)
str += "**∞**";
else
str += $"【{(int)TotalLength.TotalMinutes}m {TotalLength.Seconds}s】";
return str;
}
const int milliseconds = 20; const int milliseconds = 20;
const int samplesPerFrame = (48000 / 1000) * milliseconds; const int samplesPerFrame = (48000 / 1000) * milliseconds;
@ -54,11 +40,71 @@ namespace NadekoBot.Modules.Music.Classes
private ulong bytesSent { get; set; } = 0; private ulong bytesSent { get; set; } = 0;
public bool PrintStatusMessage { get; set; } = true; //pwetty
public string PrettyProvider =>
$"{(SongInfo.Provider ?? "No Provider")}";
public string PrettyFullTime => PrettyCurrentTime + " / " + PrettyTotalTime;
public string PrettyName => $"**[{SongInfo.Title.TrimTo(65)}]({songUrl})**";
public string PrettyInfo => $"{MusicPlayer.PrettyVolume} | {PrettyTotalTime} | {PrettyProvider} | {QueuerName}";
public string PrettyFullName => $"{PrettyName}\n\t\t`{PrettyTotalTime} | {PrettyProvider} | {QueuerName}`";
public string PrettyCurrentTime => CurrentTime.ToString(@"mm\:ss");
private string PrettyTotalTime {
get {
if (TotalTime == TimeSpan.Zero)
return "(?)";
else if (TotalTime == TimeSpan.MaxValue)
return "∞";
else
return TotalTime.ToString(@"mm\:ss");
}
}
public string Thumbnail {
get {
switch (SongInfo.ProviderType)
{
case MusicType.Radio:
//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.Normal:
return $"https://cdn.discordapp.com/attachments/155726317222887425/261850925063340032/1482522097_radio.png"; //test links
case MusicType.Local:
return $"https://cdn.discordapp.com/attachments/155726317222887425/261850914783100928/1482522077_music.png"; //test links
case MusicType.Soundcloud:
return SongInfo.AlbumArt;
default:
return "";
}
}
}
private string songUrl {
get {
switch (SongInfo.ProviderType)
{
case MusicType.Normal:
return SongInfo.Query;
case MusicType.Soundcloud:
return SongInfo.Query;
case MusicType.Local:
return $"https://google.com/search?q={ WebUtility.UrlEncode(SongInfo.Title).Replace(' ', '+') }";
case MusicType.Radio:
return $"https://google.com/search?q={SongInfo.Title}";
default:
return "";
}
}
}
private int skipTo = 0; private int skipTo = 0;
private Logger _log;
public int SkipTo { public int SkipTo {
get { return skipTo; } get { return skipTo; }
set { set {
@ -67,7 +113,7 @@ namespace NadekoBot.Modules.Music.Classes
} }
} }
public TimeSpan TotalLength { get; set; } = TimeSpan.Zero; private readonly Logger _log;
public Song(SongInfo songInfo) public Song(SongInfo songInfo)
{ {
@ -79,16 +125,10 @@ namespace NadekoBot.Modules.Music.Classes
{ {
var s = new Song(SongInfo); var s = new Song(SongInfo);
s.MusicPlayer = MusicPlayer; s.MusicPlayer = MusicPlayer;
s.State = StreamState.Queued; s.QueuerName = QueuerName;
return s; return s;
} }
public Song SetMusicPlayer(MusicPlayer mp)
{
this.MusicPlayer = mp;
return this;
}
public async Task Play(IAudioClient voiceClient, CancellationToken cancelToken) public async Task Play(IAudioClient voiceClient, CancellationToken cancelToken)
{ {
var filename = Path.Combine(Music.MusicDataPath, DateTime.Now.UnixTimestamp().ToString()); var filename = Path.Combine(Music.MusicDataPath, DateTime.Now.UnixTimestamp().ToString());
@ -96,8 +136,6 @@ namespace NadekoBot.Modules.Music.Classes
SongBuffer inStream = new SongBuffer(MusicPlayer, filename, SongInfo, skipTo, frameBytes * 100); SongBuffer inStream = new SongBuffer(MusicPlayer, filename, SongInfo, skipTo, frameBytes * 100);
var bufferTask = inStream.BufferSong(cancelToken).ConfigureAwait(false); var bufferTask = inStream.BufferSong(cancelToken).ConfigureAwait(false);
bytesSent = 0;
try try
{ {
var attempt = 0; var attempt = 0;
@ -147,7 +185,8 @@ namespace NadekoBot.Modules.Music.Classes
int nextTime = Environment.TickCount + milliseconds; int nextTime = Environment.TickCount + milliseconds;
byte[] buffer = new byte[frameBytes]; byte[] buffer = new byte[frameBytes];
while (!cancelToken.IsCancellationRequested) 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---------"); //Console.WriteLine($"Read: {songBuffer.ReadPosition}\nWrite: {songBuffer.WritePosition}\nContentLength:{songBuffer.ContentLength}\n---------");
var read = await inStream.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false); var read = await inStream.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false);
@ -172,10 +211,15 @@ namespace NadekoBot.Modules.Music.Classes
if (slowconnection) if (slowconnection)
{ {
_log.Warn("Slow connection has disrupted music, waiting a bit for buffer"); _log.Warn("Slow connection has disrupted music, waiting a bit for buffer");
await Task.Delay(1000, cancelToken).ConfigureAwait(false); await Task.Delay(1000, cancelToken).ConfigureAwait(false);
nextTime = Environment.TickCount + milliseconds;
} }
else else
{
await Task.Delay(100, cancelToken).ConfigureAwait(false); await Task.Delay(100, cancelToken).ConfigureAwait(false);
nextTime = Environment.TickCount + milliseconds;
}
} }
else else
attempt = 0; attempt = 0;
@ -184,7 +228,10 @@ namespace NadekoBot.Modules.Music.Classes
attempt = 0; attempt = 0;
while (this.MusicPlayer.Paused) while (this.MusicPlayer.Paused)
{
await Task.Delay(200, cancelToken).ConfigureAwait(false); await Task.Delay(200, cancelToken).ConfigureAwait(false);
nextTime = Environment.TickCount + milliseconds;
}
buffer = AdjustVolume(buffer, MusicPlayer.Volume); buffer = AdjustVolume(buffer, MusicPlayer.Volume);
@ -213,35 +260,6 @@ namespace NadekoBot.Modules.Music.Classes
_log.Debug("Buffering successfull"); _log.Debug("Buffering successfull");
} }
/*
//stackoverflow ftw
private static byte[] AdjustVolume(byte[] audioSamples, float volume)
{
if (Math.Abs(volume - 1.0f) < 0.01f)
return audioSamples;
var array = new byte[audioSamples.Length];
for (var i = 0; i < array.Length; i += 2)
{
// convert byte pair to int
short buf1 = audioSamples[i + 1];
short buf2 = audioSamples[i];
buf1 = (short)((buf1 & 0xff) << 8);
buf2 = (short)(buf2 & 0xff);
var res = (short)(buf1 | buf2);
res = (short)(res * volume);
// convert back
array[i] = (byte)res;
array[i + 1] = (byte)(res >> 8);
}
return array;
}
*/
//aidiakapi ftw //aidiakapi ftw
public unsafe static byte[] AdjustVolume(byte[] audioSamples, float volume) public unsafe static byte[] AdjustVolume(byte[] audioSamples, float volume)
{ {
@ -267,200 +285,5 @@ namespace NadekoBot.Modules.Music.Classes
return audioSamples; return audioSamples;
} }
public static async Task<Song> ResolveSong(string query, MusicType musicType = MusicType.Normal)
{
if (string.IsNullOrWhiteSpace(query))
throw new ArgumentNullException(nameof(query));
if (musicType != MusicType.Local && IsRadioLink(query))
{
musicType = MusicType.Radio;
query = await HandleStreamContainers(query).ConfigureAwait(false) ?? query;
}
try
{
switch (musicType)
{
case MusicType.Local:
return new Song(new SongInfo
{
Uri = "\"" + Path.GetFullPath(query) + "\"",
Title = Path.GetFileNameWithoutExtension(query),
Provider = "Local File",
ProviderType = musicType,
Query = query,
});
case MusicType.Radio:
return new Song(new SongInfo
{
Uri = query,
Title = $"{query}",
Provider = "Radio Stream",
ProviderType = musicType,
Query = query
})
{ TotalLength = TimeSpan.MaxValue };
}
if (SoundCloud.Default.IsSoundCloudLink(query))
{
var svideo = await SoundCloud.Default.ResolveVideoAsync(query).ConfigureAwait(false);
return new Song(new SongInfo
{
Title = svideo.FullName,
Provider = "SoundCloud",
Uri = svideo.StreamLink,
ProviderType = musicType,
Query = svideo.TrackLink,
})
{ TotalLength = TimeSpan.FromMilliseconds(svideo.Duration) };
}
if (musicType == MusicType.Soundcloud)
{
var svideo = await SoundCloud.Default.GetVideoByQueryAsync(query).ConfigureAwait(false);
return new Song(new SongInfo
{
Title = svideo.FullName,
Provider = "SoundCloud",
Uri = svideo.StreamLink,
ProviderType = MusicType.Normal,
Query = svideo.TrackLink,
})
{ TotalLength = TimeSpan.FromMilliseconds(svideo.Duration) };
}
var link = (await NadekoBot.Google.GetVideosByKeywordsAsync(query).ConfigureAwait(false)).FirstOrDefault();
if (string.IsNullOrWhiteSpace(link))
throw new OperationCanceledException("Not a valid youtube query.");
var allVideos = await Task.Run(async () => { try { return await YouTube.Default.GetAllVideosAsync(link).ConfigureAwait(false); } catch { return Enumerable.Empty<YouTubeVideo>(); } }).ConfigureAwait(false);
var videos = allVideos.Where(v => v.AdaptiveKind == AdaptiveKind.Audio);
var video = videos
.Where(v => v.AudioBitrate < 256)
.OrderByDescending(v => v.AudioBitrate)
.FirstOrDefault();
if (video == null) // do something with this error
throw new Exception("Could not load any video elements based on the query.");
var m = Regex.Match(query, @"\?t=(?<t>\d*)");
int gotoTime = 0;
if (m.Captures.Count > 0)
int.TryParse(m.Groups["t"].ToString(), out gotoTime);
var song = new Song(new SongInfo
{
Title = video.Title.Substring(0, video.Title.Length - 10), // removing trailing "- You Tube"
Provider = "YouTube",
Uri = video.Uri,
Query = link,
ProviderType = musicType,
});
song.SkipTo = gotoTime;
return song;
}
catch (Exception ex)
{
Console.WriteLine($"Failed resolving the link.{ex.Message}");
return null;
}
}
private static async Task<string> HandleStreamContainers(string query)
{
string file = null;
try
{
using (var http = new HttpClient())
{
file = await http.GetStringAsync(query).ConfigureAwait(false);
}
}
catch
{
return query;
}
if (query.Contains(".pls"))
{
//File1=http://armitunes.com:8000/
//Regex.Match(query)
try
{
var m = Regex.Match(file, "File1=(?<url>.*?)\\n");
var res = m.Groups["url"]?.ToString();
return res?.Trim();
}
catch
{
Console.WriteLine($"Failed reading .pls:\n{file}");
return null;
}
}
if (query.Contains(".m3u"))
{
/*
# This is a comment
C:\xxx4xx\xxxxxx3x\xx2xxxx\xx.mp3
C:\xxx5xx\x6xxxxxx\x7xxxxx\xx.mp3
*/
try
{
var m = Regex.Match(file, "(?<url>^[^#].*)", RegexOptions.Multiline);
var res = m.Groups["url"]?.ToString();
return res?.Trim();
}
catch
{
Console.WriteLine($"Failed reading .m3u:\n{file}");
return null;
}
}
if (query.Contains(".asx"))
{
//<ref href="http://armitunes.com:8000"/>
try
{
var m = Regex.Match(file, "<ref href=\"(?<url>.*?)\"");
var res = m.Groups["url"]?.ToString();
return res?.Trim();
}
catch
{
Console.WriteLine($"Failed reading .asx:\n{file}");
return null;
}
}
if (query.Contains(".xspf"))
{
/*
<?xml version="1.0" encoding="UTF-8"?>
<playlist version="1" xmlns="http://xspf.org/ns/0/">
<trackList>
<track><location>file:///mp3s/song_1.mp3</location></track>
*/
try
{
var m = Regex.Match(file, "<location>(?<url>.*?)</location>");
var res = m.Groups["url"]?.ToString();
return res?.Trim();
}
catch
{
Console.WriteLine($"Failed reading .xspf:\n{file}");
return null;
}
}
return query;
}
private static bool IsRadioLink(string query) =>
(query.StartsWith("http") ||
query.StartsWith("ww"))
&&
(query.Contains(".pls") ||
query.Contains(".m3u") ||
query.Contains(".asx") ||
query.Contains(".xspf"));
} }
} }

View File

@ -25,15 +25,15 @@ namespace NadekoBot.Modules.Music.Classes
_log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
} }
MusicPlayer MusicPlayer; MusicPlayer MusicPlayer { get; }
private string Basename; private string Basename { get; }
private SongInfo SongInfo; private SongInfo SongInfo { get; }
private int SkipTo; private int SkipTo { get; }
private int MaxFileSize = 2.MiB(); private int MaxFileSize { get; } = 2.MiB();
private long FileNumber = -1; private long FileNumber = -1;
@ -76,7 +76,8 @@ namespace NadekoBot.Modules.Music.Classes
try try
{ {
outStream.Dispose(); outStream.Dispose();
}catch { } }
catch { }
outStream = new FileStream(Basename + "-" + ++FileNumber, FileMode.Append, FileAccess.Write, FileShare.Read); outStream = new FileStream(Basename + "-" + ++FileNumber, FileMode.Append, FileAccess.Write, FileShare.Read);
currentFileSize = bytesRead; currentFileSize = bytesRead;
} }

View File

@ -0,0 +1,212 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using VideoLibrary;
namespace NadekoBot.Modules.Music.Classes
{
public static class SongHandler
{
public static async Task<Song> ResolveSong(string query, MusicType musicType = MusicType.Normal)
{
if (string.IsNullOrWhiteSpace(query))
throw new ArgumentNullException(nameof(query));
if (musicType != MusicType.Local && IsRadioLink(query))
{
musicType = MusicType.Radio;
query = await HandleStreamContainers(query).ConfigureAwait(false) ?? query;
}
try
{
switch (musicType)
{
case MusicType.Local:
return new Song(new SongInfo
{
Uri = "\"" + Path.GetFullPath(query) + "\"",
Title = Path.GetFileNameWithoutExtension(query),
Provider = "Local File",
ProviderType = musicType,
Query = query,
});
case MusicType.Radio:
return new Song(new SongInfo
{
Uri = query,
Title = $"{query}",
Provider = "Radio Stream",
ProviderType = musicType,
Query = query
})
{ TotalTime = TimeSpan.MaxValue };
}
if (SoundCloud.Default.IsSoundCloudLink(query))
{
var svideo = await SoundCloud.Default.ResolveVideoAsync(query).ConfigureAwait(false);
return new Song(new SongInfo
{
Title = svideo.FullName,
Provider = "SoundCloud",
Uri = svideo.StreamLink,
ProviderType = musicType,
Query = svideo.TrackLink,
AlbumArt = svideo.artwork_url,
})
{ TotalTime = TimeSpan.FromMilliseconds(svideo.Duration) };
}
if (musicType == MusicType.Soundcloud)
{
var svideo = await SoundCloud.Default.GetVideoByQueryAsync(query).ConfigureAwait(false);
return new Song(new SongInfo
{
Title = svideo.FullName,
Provider = "SoundCloud",
Uri = svideo.StreamLink,
ProviderType = MusicType.Soundcloud,
Query = svideo.TrackLink,
AlbumArt = svideo.artwork_url,
})
{ TotalTime = TimeSpan.FromMilliseconds(svideo.Duration) };
}
var link = (await NadekoBot.Google.GetVideosByKeywordsAsync(query).ConfigureAwait(false)).FirstOrDefault();
if (string.IsNullOrWhiteSpace(link))
throw new OperationCanceledException("Not a valid youtube query.");
var allVideos = await Task.Run(async () => { try { return await YouTube.Default.GetAllVideosAsync(link).ConfigureAwait(false); } catch { return Enumerable.Empty<YouTubeVideo>(); } }).ConfigureAwait(false);
var videos = allVideos.Where(v => v.AdaptiveKind == AdaptiveKind.Audio);
var video = videos
.Where(v => v.AudioBitrate < 256)
.OrderByDescending(v => v.AudioBitrate)
.FirstOrDefault();
if (video == null) // do something with this error
throw new Exception("Could not load any video elements based on the query.");
var m = Regex.Match(query, @"\?t=(?<t>\d*)");
int gotoTime = 0;
if (m.Captures.Count > 0)
int.TryParse(m.Groups["t"].ToString(), out gotoTime);
var song = new Song(new SongInfo
{
Title = video.Title.Substring(0, video.Title.Length - 10), // removing trailing "- You Tube"
Provider = "YouTube",
Uri = video.Uri,
Query = link,
ProviderType = musicType,
});
song.SkipTo = gotoTime;
return song;
}
catch (Exception ex)
{
Console.WriteLine($"Failed resolving the link.{ex.Message}");
return null;
}
}
private static async Task<string> HandleStreamContainers(string query)
{
string file = null;
try
{
using (var http = new HttpClient())
{
file = await http.GetStringAsync(query).ConfigureAwait(false);
}
}
catch
{
return query;
}
if (query.Contains(".pls"))
{
//File1=http://armitunes.com:8000/
//Regex.Match(query)
try
{
var m = Regex.Match(file, "File1=(?<url>.*?)\\n");
var res = m.Groups["url"]?.ToString();
return res?.Trim();
}
catch
{
Console.WriteLine($"Failed reading .pls:\n{file}");
return null;
}
}
if (query.Contains(".m3u"))
{
/*
# This is a comment
C:\xxx4xx\xxxxxx3x\xx2xxxx\xx.mp3
C:\xxx5xx\x6xxxxxx\x7xxxxx\xx.mp3
*/
try
{
var m = Regex.Match(file, "(?<url>^[^#].*)", RegexOptions.Multiline);
var res = m.Groups["url"]?.ToString();
return res?.Trim();
}
catch
{
Console.WriteLine($"Failed reading .m3u:\n{file}");
return null;
}
}
if (query.Contains(".asx"))
{
//<ref href="http://armitunes.com:8000"/>
try
{
var m = Regex.Match(file, "<ref href=\"(?<url>.*?)\"");
var res = m.Groups["url"]?.ToString();
return res?.Trim();
}
catch
{
Console.WriteLine($"Failed reading .asx:\n{file}");
return null;
}
}
if (query.Contains(".xspf"))
{
/*
<?xml version="1.0" encoding="UTF-8"?>
<playlist version="1" xmlns="http://xspf.org/ns/0/">
<trackList>
<track><location>file:///mp3s/song_1.mp3</location></track>
*/
try
{
var m = Regex.Match(file, "<location>(?<url>.*?)</location>");
var res = m.Groups["url"]?.ToString();
return res?.Trim();
}
catch
{
Console.WriteLine($"Failed reading .xspf:\n{file}");
return null;
}
}
return query;
}
private static bool IsRadioLink(string query) =>
(query.StartsWith("http") ||
query.StartsWith("ww"))
&&
(query.Contains(".pls") ||
query.Contains(".m3u") ||
query.Contains(".asx") ||
query.Contains(".xspf"));
}
}

View File

@ -73,6 +73,7 @@ namespace NadekoBot.Modules.Music.Classes
public int Duration { get; set; } public int Duration { get; set; }
[JsonProperty("permalink_url")] [JsonProperty("permalink_url")]
public string TrackLink { get; set; } = ""; public string TrackLink { get; set; } = "";
public string artwork_url { get; set; } = "";
[JsonIgnore] [JsonIgnore]
public string StreamLink => $"https://api.soundcloud.com/tracks/{Id}/stream?client_id={NadekoBot.Credentials.SoundCloudClientId}"; public string StreamLink => $"https://api.soundcloud.com/tracks/{Id}/stream?client_id={NadekoBot.Credentials.SoundCloudClientId}";
} }

View File

@ -14,25 +14,47 @@ using System.Net.Http;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using System.Collections.Generic; using System.Collections.Generic;
using NadekoBot.Services.Database.Models; using NadekoBot.Services.Database.Models;
using System.Text.RegularExpressions;
using System.Threading;
namespace NadekoBot.Modules.Music namespace NadekoBot.Modules.Music
{ {
[NadekoModule("Music", "!!", AutoLoad = false)] [NadekoModule("Music", "!!", AutoLoad = false)]
public partial class Music : DiscordModule public partial class Music : DiscordModule
{ {
public static ConcurrentDictionary<ulong, MusicPlayer> MusicPlayers = new ConcurrentDictionary<ulong, MusicPlayer>(); public static ConcurrentDictionary<ulong, MusicPlayer> MusicPlayers { get; } = new ConcurrentDictionary<ulong, MusicPlayer>();
public const string MusicDataPath = "data/musicdata"; public const string MusicDataPath = "data/musicdata";
private IGoogleApiService _google;
public Music(ILocalization loc, CommandService cmds, ShardedDiscordClient client, IGoogleApiService google) : base(loc, cmds, client) public Music() : base()
{ {
//it can fail if its currenctly opened or doesn't exist. Either way i don't care //it can fail if its currenctly opened or doesn't exist. Either way i don't care
try { Directory.Delete(MusicDataPath, true); } catch { } try { Directory.Delete(MusicDataPath, true); } catch { }
Directory.CreateDirectory(MusicDataPath); NadekoBot.Client.UserVoiceStateUpdated += Client_UserVoiceStateUpdated;
_google = google; Directory.CreateDirectory(MusicDataPath);
}
private void Client_UserVoiceStateUpdated(IUser iusr, IVoiceState oldState, IVoiceState newState)
{
var usr = iusr as IGuildUser;
if (usr == null ||
oldState.VoiceChannel == newState.VoiceChannel)
return;
MusicPlayer player;
if (!MusicPlayers.TryGetValue(usr.Guild.Id, out player))
return;
if ((player.PlaybackVoiceChannel == newState.VoiceChannel && //if joined first, and player paused, unpause
player.Paused &&
player.PlaybackVoiceChannel.GetUsers().Count == 2) || // keep in mind bot is in the channel (+1)
(player.PlaybackVoiceChannel == oldState.VoiceChannel && // if left last, and player unpaused, pause
!player.Paused &&
player.PlaybackVoiceChannel.GetUsers().Count == 1))
{
player.TogglePause();
}
return;
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -75,33 +97,45 @@ namespace NadekoBot.Modules.Music
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public Task Destroy(IUserMessage umsg) public async Task Destroy(IUserMessage umsg)
{
var channel = (ITextChannel)umsg.Channel;
await channel.SendErrorAsync("This command is temporarily disabled.").ConfigureAwait(false);
/*MusicPlayer musicPlayer;
if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer)) return Task.CompletedTask;
if (((IGuildUser)umsg.Author).VoiceChannel == musicPlayer.PlaybackVoiceChannel)
if(MusicPlayers.TryRemove(channel.Guild.Id, out musicPlayer))
musicPlayer.Destroy();
return Task.CompletedTask;*/
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public Task Pause(IUserMessage umsg)
{ {
var channel = (ITextChannel)umsg.Channel; var channel = (ITextChannel)umsg.Channel;
MusicPlayer musicPlayer; MusicPlayer musicPlayer;
if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer)) return Task.CompletedTask; if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer)) return Task.CompletedTask;
if (((IGuildUser)umsg.Author).VoiceChannel == musicPlayer.PlaybackVoiceChannel) if (((IGuildUser)umsg.Author).VoiceChannel != musicPlayer.PlaybackVoiceChannel)
if(MusicPlayers.TryRemove(channel.Guild.Id, out musicPlayer)) return Task.CompletedTask;
musicPlayer.Destroy(); musicPlayer.TogglePause();
return Task.CompletedTask; return Task.CompletedTask;
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task Pause(IUserMessage umsg) public async Task Fairplay(IUserMessage umsg)
{ {
var channel = (ITextChannel)umsg.Channel; var channel = (ITextChannel)umsg.Channel;
MusicPlayer musicPlayer; MusicPlayer musicPlayer;
if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer)) return; if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer)) return;
if (((IGuildUser)umsg.Author).VoiceChannel != musicPlayer.PlaybackVoiceChannel) if (((IGuildUser)umsg.Author).VoiceChannel != musicPlayer.PlaybackVoiceChannel)
return; return;
musicPlayer.TogglePause(); var val = musicPlayer.FairPlay = !musicPlayer.FairPlay;
if (musicPlayer.Paused)
await channel.SendMessageAsync("🎵`Music Player paused.`").ConfigureAwait(false); await channel.SendConfirmAsync("Fair play " + (val ? "enabled" : "disabled") + ".").ConfigureAwait(false);
else
await channel.SendMessageAsync("🎵`Music Player unpaused.`").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -113,8 +147,7 @@ namespace NadekoBot.Modules.Music
await QueueSong(((IGuildUser)umsg.Author), channel, ((IGuildUser)umsg.Author).VoiceChannel, query).ConfigureAwait(false); await QueueSong(((IGuildUser)umsg.Author), channel, ((IGuildUser)umsg.Author).VoiceChannel, query).ConfigureAwait(false);
if (channel.Guild.GetCurrentUser().GetPermissions(channel).ManageMessages) if (channel.Guild.GetCurrentUser().GetPermissions(channel).ManageMessages)
{ {
await Task.Delay(10000).ConfigureAwait(false); umsg.DeleteAfter(10);
await ((IUserMessage)umsg).DeleteAsync().ConfigureAwait(false);
} }
} }
@ -127,8 +160,7 @@ namespace NadekoBot.Modules.Music
await QueueSong(((IGuildUser)umsg.Author), channel, ((IGuildUser)umsg.Author).VoiceChannel, query, musicType: MusicType.Soundcloud).ConfigureAwait(false); await QueueSong(((IGuildUser)umsg.Author), channel, ((IGuildUser)umsg.Author).VoiceChannel, query, musicType: MusicType.Soundcloud).ConfigureAwait(false);
if (channel.Guild.GetCurrentUser().GetPermissions(channel).ManageMessages) if (channel.Guild.GetCurrentUser().GetPermissions(channel).ManageMessages)
{ {
await Task.Delay(10000).ConfigureAwait(false); umsg.DeleteAfter(10);
await ((IUserMessage)umsg).DeleteAsync().ConfigureAwait(false);
} }
} }
@ -140,7 +172,7 @@ namespace NadekoBot.Modules.Music
MusicPlayer musicPlayer; MusicPlayer musicPlayer;
if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer)) if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer))
{ {
await channel.SendMessageAsync("🎵 No active music player.").ConfigureAwait(false); await channel.SendErrorAsync("🎵 No active music player.").ConfigureAwait(false);
return; return;
} }
if (page <= 0) if (page <= 0)
@ -148,27 +180,44 @@ namespace NadekoBot.Modules.Music
var currentSong = musicPlayer.CurrentSong; var currentSong = musicPlayer.CurrentSong;
if (currentSong == null) if (currentSong == null)
return;
if (currentSong.TotalLength == TimeSpan.Zero)
{ {
await musicPlayer.UpdateSongDurationsAsync().ConfigureAwait(false); await channel.SendErrorAsync("🎵 No active music player.").ConfigureAwait(false);
return;
} }
var toSend = $"🎵`Now Playing` {currentSong.PrettyName} " + $"{currentSong.PrettyCurrentTime()}\n"; try { await musicPlayer.UpdateSongDurationsAsync().ConfigureAwait(false); } catch { }
if (musicPlayer.RepeatSong)
toSend += "🔂"; const int itemsPerPage = 10;
else if (musicPlayer.RepeatPlaylist)
toSend += "🔁";
toSend += $" **{musicPlayer.Playlist.Count}** `tracks currently queued. Showing page {page}` ";
if (musicPlayer.MaxQueueSize != 0 && musicPlayer.Playlist.Count >= musicPlayer.MaxQueueSize)
toSend += "**Song queue is full!**\n";
else
toSend += "\n";
const int itemsPerPage = 15;
int startAt = itemsPerPage * (page - 1); int startAt = itemsPerPage * (page - 1);
var number = 1 + startAt; var number = 0 + startAt;
await channel.SendMessageAsync(toSend + string.Join("\n", musicPlayer.Playlist.Skip(startAt).Take(15).Select(v => $"`{number++}.` {v.PrettyName}"))).ConfigureAwait(false);
var total = musicPlayer.TotalPlaytime;
var maxPlaytime = musicPlayer.MaxPlaytimeSeconds;
var embed = new EmbedBuilder()
.WithAuthor(eab => eab.WithName($"Player Queue - Page {page}")
.WithMusicIcon())
.WithDescription(string.Join("\n", musicPlayer.Playlist
.Skip(startAt)
.Take(10)
.Select(v => $"`{++number}.` {v.PrettyFullName}")))
.WithFooter(ef => ef.WithText($"{musicPlayer.PrettyVolume} | {musicPlayer.Playlist.Count} " +
$"{("tracks".SnPl(musicPlayer.Playlist.Count))} | {(int)total.TotalHours}h {total.Minutes}m {total.Seconds}s | " +
(musicPlayer.FairPlay? "✔fairplay" : "✖fairplay") + $" | " + (maxPlaytime == 0 ? "unlimited" : $"{maxPlaytime}s limit")))
.WithOkColor();
if (musicPlayer.RepeatSong)
{
embed.WithTitle($"🔂 Repeating Song: {currentSong.SongInfo.Title} | {currentSong.PrettyFullTime}");
}
else if (musicPlayer.RepeatPlaylist)
{
embed.WithTitle("🔁 Repeating Playlist");
}
if (musicPlayer.MaxQueueSize != 0 && musicPlayer.Playlist.Count >= musicPlayer.MaxQueueSize)
{
embed.WithTitle("🎵 Song queue is full!");
}
await channel.EmbedAsync(embed.Build()).ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -176,19 +225,22 @@ namespace NadekoBot.Modules.Music
public async Task NowPlaying(IUserMessage umsg) public async Task NowPlaying(IUserMessage umsg)
{ {
var channel = (ITextChannel)umsg.Channel; var channel = (ITextChannel)umsg.Channel;
MusicPlayer musicPlayer; MusicPlayer musicPlayer;
if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer)) if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer))
return; return;
var currentSong = musicPlayer.CurrentSong; var currentSong = musicPlayer.CurrentSong;
if (currentSong == null) if (currentSong == null)
return; return;
try { await musicPlayer.UpdateSongDurationsAsync().ConfigureAwait(false); } catch { }
if (currentSong.TotalLength == TimeSpan.Zero) var embed = new EmbedBuilder().WithOkColor()
{ .WithAuthor(eab => eab.WithName("Now Playing").WithMusicIcon())
await musicPlayer.UpdateSongDurationsAsync().ConfigureAwait(false); .WithDescription(currentSong.PrettyName)
} .WithThumbnail(tn => tn.Url = currentSong.Thumbnail)
await channel.SendMessageAsync($"🎵`Now Playing` {currentSong.PrettyName} " + .WithFooter(ef => ef.WithText(musicPlayer.PrettyVolume + " | " + currentSong.PrettyFullTime + $" | {currentSong.PrettyProvider} | {currentSong.QueuerName}"));
$"{currentSong.PrettyCurrentTime()}").ConfigureAwait(false);
await channel.EmbedAsync(embed.Build()).ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -204,7 +256,7 @@ namespace NadekoBot.Modules.Music
if (val < 0) if (val < 0)
return; return;
var volume = musicPlayer.SetVolume(val); var volume = musicPlayer.SetVolume(val);
await channel.SendMessageAsync($"🎵 `Volume set to {volume}%`").ConfigureAwait(false); await channel.SendConfirmAsync($"🎵 Volume set to {volume}%").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -215,15 +267,15 @@ namespace NadekoBot.Modules.Music
if (val < 0 || val > 100) if (val < 0 || val > 100)
{ {
await channel.SendMessageAsync("Volume number invalid. Must be between 0 and 100").ConfigureAwait(false); await channel.SendErrorAsync("Volume number invalid. Must be between 0 and 100").ConfigureAwait(false);
return; return;
} }
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
uow.GuildConfigs.For(channel.Guild.Id).DefaultMusicVolume = val / 100.0f; uow.GuildConfigs.For(channel.Guild.Id, set => set).DefaultMusicVolume = val / 100.0f;
uow.Complete(); uow.Complete();
} }
await channel.SendMessageAsync($"🎵 `Default volume set to {val}%`").ConfigureAwait(false); await channel.SendConfirmAsync($"🎵 Default volume set to {val}%").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -238,12 +290,12 @@ namespace NadekoBot.Modules.Music
return; return;
if (musicPlayer.Playlist.Count < 2) if (musicPlayer.Playlist.Count < 2)
{ {
await channel.SendMessageAsync("💢 Not enough songs in order to perform the shuffle.").ConfigureAwait(false); await channel.SendErrorAsync("💢 Not enough songs in order to perform the shuffle.").ConfigureAwait(false);
return; return;
} }
musicPlayer.Shuffle(); musicPlayer.Shuffle();
await channel.SendMessageAsync("🎵 `Songs shuffled.`").ConfigureAwait(false); await channel.SendConfirmAsync("🎵 Songs shuffled.").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -256,35 +308,48 @@ namespace NadekoBot.Modules.Music
return; return;
if (((IGuildUser)umsg.Author).VoiceChannel?.Guild != channel.Guild) if (((IGuildUser)umsg.Author).VoiceChannel?.Guild != channel.Guild)
{ {
await channel.SendMessageAsync("💢 You need to be in a voice channel on this server.\n If you are already in a voice channel, try rejoining it.").ConfigureAwait(false); await channel.SendErrorAsync("💢 You need to be in a **voice channel** on this server.\n If you are already in a voice channel, try rejoining it.").ConfigureAwait(false);
return; return;
} }
var plId = (await _google.GetPlaylistIdsByKeywordsAsync(arg).ConfigureAwait(false)).FirstOrDefault(); var plId = (await NadekoBot.Google.GetPlaylistIdsByKeywordsAsync(arg).ConfigureAwait(false)).FirstOrDefault();
if (plId == null) if (plId == null)
{ {
await channel.SendMessageAsync("No search results for that query."); await channel.SendErrorAsync("No search results for that query.");
return; return;
} }
var ids = await _google.GetPlaylistTracksAsync(plId, 500).ConfigureAwait(false); var ids = await NadekoBot.Google.GetPlaylistTracksAsync(plId, 500).ConfigureAwait(false);
if (!ids.Any()) if (!ids.Any())
{ {
await channel.SendMessageAsync($"🎵 `Failed to find any songs.`").ConfigureAwait(false); await channel.SendErrorAsync($"🎵 Failed to find any songs.").ConfigureAwait(false);
return; return;
} }
var idArray = ids as string[] ?? ids.ToArray(); var count = ids.Count();
var count = idArray.Length;
var msg = var msg = await channel.SendMessageAsync($"🎵 Attempting to queue **{count}** songs".SnPl(count) + "...").ConfigureAwait(false);
await channel.SendMessageAsync($"🎵 `Attempting to queue {count} songs".SnPl(count) + "...`").ConfigureAwait(false);
foreach (var id in idArray) var cancelSource = new CancellationTokenSource();
var gusr = (IGuildUser)umsg.Author;
while (ids.Any() && !cancelSource.IsCancellationRequested)
{ {
var tasks = Task.WhenAll(ids.Take(5).Select(async id =>
{
if (cancelSource.Token.IsCancellationRequested)
return;
try try
{ {
await QueueSong(((IGuildUser)umsg.Author), channel, ((IGuildUser)umsg.Author).VoiceChannel, id, true).ConfigureAwait(false); await QueueSong(gusr, channel, gusr.VoiceChannel, id, true).ConfigureAwait(false);
} }
catch (SongNotFoundException) { } catch (SongNotFoundException) { }
catch { break; } catch { try { cancelSource.Cancel(); } catch { } }
}));
await Task.WhenAny(tasks, Task.Delay(Timeout.Infinite, cancelSource.Token));
ids = ids.Skip(5);
} }
await msg.ModifyAsync(m => m.Content = "🎵 `Playlist queue complete.`").ConfigureAwait(false);
await msg.ModifyAsync(m => m.Content = "✅ Playlist queue complete.").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -338,11 +403,12 @@ namespace NadekoBot.Modules.Music
var dir = new DirectoryInfo(arg); var dir = new DirectoryInfo(arg);
var fileEnum = dir.GetFiles("*", SearchOption.AllDirectories) var fileEnum = dir.GetFiles("*", SearchOption.AllDirectories)
.Where(x => !x.Attributes.HasFlag(FileAttributes.Hidden | FileAttributes.System)); .Where(x => !x.Attributes.HasFlag(FileAttributes.Hidden | FileAttributes.System));
var gusr = (IGuildUser)umsg.Author;
foreach (var file in fileEnum) foreach (var file in fileEnum)
{ {
try try
{ {
await QueueSong(((IGuildUser)umsg.Author), channel, ((IGuildUser)umsg.Author).VoiceChannel, file.FullName, true, MusicType.Local).ConfigureAwait(false); await QueueSong(gusr, channel, gusr.VoiceChannel, file.FullName, true, MusicType.Local).ConfigureAwait(false);
} }
catch (PlaylistFullException) catch (PlaylistFullException)
{ {
@ -350,7 +416,7 @@ namespace NadekoBot.Modules.Music
} }
catch { } catch { }
} }
await channel.SendMessageAsync("🎵 `Directory queue complete.`").ConfigureAwait(false); await channel.SendConfirmAsync("🎵 Directory queue complete.").ConfigureAwait(false);
} }
catch { } catch { }
} }
@ -362,14 +428,13 @@ namespace NadekoBot.Modules.Music
var channel = (ITextChannel)umsg.Channel; var channel = (ITextChannel)umsg.Channel;
if (((IGuildUser)umsg.Author).VoiceChannel?.Guild != channel.Guild) if (((IGuildUser)umsg.Author).VoiceChannel?.Guild != channel.Guild)
{ {
await channel.SendMessageAsync("💢 You need to be in a voice channel on this server.\n If you are already in a voice channel, try rejoining it.").ConfigureAwait(false); await channel.SendErrorAsync("💢 You need to be in a voice channel on this server.\n If you are already in a voice channel, try rejoining it.").ConfigureAwait(false);
return; return;
} }
await QueueSong(((IGuildUser)umsg.Author), channel, ((IGuildUser)umsg.Author).VoiceChannel, radio_link, musicType: MusicType.Radio).ConfigureAwait(false); await QueueSong(((IGuildUser)umsg.Author), channel, ((IGuildUser)umsg.Author).VoiceChannel, radio_link, musicType: MusicType.Radio).ConfigureAwait(false);
if (channel.Guild.GetCurrentUser().GetPermissions(channel).ManageMessages) if (channel.Guild.GetCurrentUser().GetPermissions(channel).ManageMessages)
{ {
await Task.Delay(10000).ConfigureAwait(false); umsg.DeleteAfter(10);
await ((IUserMessage)umsg).DeleteAsync().ConfigureAwait(false);
} }
} }
@ -416,7 +481,15 @@ namespace NadekoBot.Modules.Music
return; return;
var song = (musicPlayer.Playlist as List<Song>)?[num - 1]; var song = (musicPlayer.Playlist as List<Song>)?[num - 1];
musicPlayer.RemoveSongAt(num - 1); musicPlayer.RemoveSongAt(num - 1);
await channel.SendMessageAsync($"🎵**Track {song.PrettyName} at position `#{num}` has been removed.**").ConfigureAwait(false);
var embed = new EmbedBuilder()
.WithAuthor(eab => eab.WithName("Song Removed!").WithMusicIcon())
.AddField(fb => fb.WithName("**Song Position**").WithValue($"#{num}").WithIsInline(true))
.AddField(fb => fb.WithName("**Song Name**").WithValue(song.PrettyName).WithIsInline(true))
.WithFooter(ef => ef.WithText($"{song.PrettyProvider} | {song.QueuerName}"))
.WithErrorColor();
await channel.EmbedAsync(embed.Build()).ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -431,7 +504,7 @@ namespace NadekoBot.Modules.Music
MusicPlayer musicPlayer; MusicPlayer musicPlayer;
if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer)) return; if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer)) return;
musicPlayer.ClearQueue(); musicPlayer.ClearQueue();
await channel.SendMessageAsync($"🎵`Queue cleared!`").ConfigureAwait(false); await channel.SendConfirmAsync($"🎵 Queue cleared!").ConfigureAwait(false);
return; return;
} }
@ -457,7 +530,7 @@ namespace NadekoBot.Modules.Music
!int.TryParse(fromtoArr[1], out n2) || n1 < 1 || n2 < 1 || n1 == n2 || !int.TryParse(fromtoArr[1], out n2) || n1 < 1 || n2 < 1 || n1 == n2 ||
n1 > playlist.Count || n2 > playlist.Count) n1 > playlist.Count || n2 > playlist.Count)
{ {
await channel.SendMessageAsync("`Invalid input.`").ConfigureAwait(false); await channel.SendErrorAsync("Invalid input.").ConfigureAwait(false);
return; return;
} }
@ -466,9 +539,15 @@ namespace NadekoBot.Modules.Music
var nn1 = n2 < n1 ? n1 : n1 - 1; var nn1 = n2 < n1 ? n1 : n1 - 1;
playlist.RemoveAt(nn1); playlist.RemoveAt(nn1);
await channel.SendMessageAsync($"🎵`Moved` {s.PrettyName} `from #{n1} to #{n2}`").ConfigureAwait(false); var embed = new EmbedBuilder()
.WithTitle(s.SongInfo.Title.TrimTo(70))
.WithUrl(s.SongInfo.Query)
.WithAuthor(eab => eab.WithName("Song Moved").WithMusicIcon())
.AddField(fb => fb.WithName("**From Position**").WithValue($"#{n1}").WithIsInline(true))
.AddField(fb => fb.WithName("**To Position**").WithValue($"#{n2}").WithIsInline(true))
.WithFooter(ef => ef.WithText($"{s.PrettyProvider} | {s.QueuerName}"))
.WithOkColor();
await channel.EmbedAsync(embed.Build()).ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -478,11 +557,27 @@ namespace NadekoBot.Modules.Music
var channel = (ITextChannel)umsg.Channel; var channel = (ITextChannel)umsg.Channel;
MusicPlayer musicPlayer; MusicPlayer musicPlayer;
if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer)) if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer))
{
return; return;
}
musicPlayer.MaxQueueSize = size; musicPlayer.MaxQueueSize = size;
await channel.SendMessageAsync($"🎵 `Max queue set to {(size == 0 ? ("unlimited") : size + " tracks")}`"); await channel.SendConfirmAsync($"🎵 Max queue set to {(size == 0 ? ("unlimited") : size + " tracks")}.");
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task SetMaxPlaytime(IUserMessage imsg, uint seconds)
{
if (seconds < 15 && seconds != 0)
return;
var channel = (ITextChannel)imsg.Channel;
MusicPlayer musicPlayer;
if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer))
return;
musicPlayer.MaxPlaytimeSeconds = seconds;
if(seconds == 0)
await channel.SendConfirmAsync($"🎵 Max playtime has no limit now.");
else
await channel.SendConfirmAsync($"🎵 Max playtime set to {seconds} seconds.");
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -497,9 +592,16 @@ namespace NadekoBot.Modules.Music
if (currentSong == null) if (currentSong == null)
return; return;
var currentValue = musicPlayer.ToggleRepeatSong(); var currentValue = musicPlayer.ToggleRepeatSong();
await channel.SendMessageAsync(currentValue ?
$"🎵🔂`Repeating track:`{currentSong.PrettyName}" : if (currentValue)
$"🎵🔂`Current track repeat stopped.`") await channel.EmbedAsync(new EmbedBuilder()
.WithOkColor()
.WithAuthor(eab => eab.WithMusicIcon().WithName("🔂 Repeating track"))
.WithDescription(currentSong.PrettyName)
.WithFooter(ef => ef.WithText(currentSong.PrettyInfo))
.Build()).ConfigureAwait(false);
else
await channel.SendConfirmAsync($"🔂 Current track repeat stopped.")
.ConfigureAwait(false); .ConfigureAwait(false);
} }
@ -512,7 +614,7 @@ namespace NadekoBot.Modules.Music
if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer)) if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer))
return; return;
var currentValue = musicPlayer.ToggleRepeatPlaylist(); var currentValue = musicPlayer.ToggleRepeatPlaylist();
await channel.SendMessageAsync($"🎵🔁`Repeat playlist {(currentValue ? "enabled" : "disabled")}`").ConfigureAwait(false); await channel.SendConfirmAsync($"🔁 Repeat playlist {(currentValue ? "**enabled**." : "**disabled**.")}").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -526,7 +628,8 @@ namespace NadekoBot.Modules.Music
var curSong = musicPlayer.CurrentSong; var curSong = musicPlayer.CurrentSong;
var songs = musicPlayer.Playlist.Append(curSong) var songs = musicPlayer.Playlist.Append(curSong)
.Select(s=> new PlaylistSong() { .Select(s => new PlaylistSong()
{
Provider = s.SongInfo.Provider, Provider = s.SongInfo.Provider,
ProviderType = s.SongInfo.ProviderType, ProviderType = s.SongInfo.ProviderType,
Title = s.SongInfo.Title, Title = s.SongInfo.Title,
@ -548,7 +651,7 @@ namespace NadekoBot.Modules.Music
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
} }
await channel.SendMessageAsync(($"🎵 `Saved playlist as {name}.` `Id: {playlist.Id}`")).ConfigureAwait(false); await channel.SendConfirmAsync(($"🎵 Saved playlist as **{name}**, ID: {playlist.Id}.")).ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -565,14 +668,15 @@ namespace NadekoBot.Modules.Music
if (mpl == null) if (mpl == null)
{ {
await channel.SendMessageAsync("`Can't find playlist with that ID`").ConfigureAwait(false); await channel.SendErrorAsync("Can't find playlist with that ID.").ConfigureAwait(false);
return; return;
} }
IUserMessage msg = null; IUserMessage msg = null;
try { msg = await channel.SendMessageAsync($"`Attempting to load {mpl.Songs.Count} songs...`").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); } try { msg = await channel.SendMessageAsync($"🎶 Attempting to load **{mpl.Songs.Count}** songs...").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
var usr = (IGuildUser)umsg.Author;
foreach (var item in mpl.Songs) foreach (var item in mpl.Songs)
{ {
var usr = (IGuildUser)umsg.Author;
try try
{ {
await QueueSong(usr, channel, usr.VoiceChannel, item.Query, true, item.ProviderType).ConfigureAwait(false); await QueueSong(usr, channel, usr.VoiceChannel, item.Query, true, item.ProviderType).ConfigureAwait(false);
@ -581,7 +685,7 @@ namespace NadekoBot.Modules.Music
catch { break; } catch { break; }
} }
if (msg != null) if (msg != null)
await msg.ModifyAsync(m => m.Content = $"`Done loading playlist {mpl.Name}.`").ConfigureAwait(false); await msg.ModifyAsync(m => m.Content = $"✅ Done loading playlist **{mpl.Name}**.").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -600,9 +704,12 @@ namespace NadekoBot.Modules.Music
playlists = uow.MusicPlaylists.GetPlaylistsOnPage(num); playlists = uow.MusicPlaylists.GetPlaylistsOnPage(num);
} }
await channel.SendMessageAsync($@"`Page {num} of saved playlists` 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)")))
.WithOkColor();
await channel.EmbedAsync(embed.Build()).ConfigureAwait(false);
" + string.Join("\n", playlists.Select(r => $"`#{r.Id}` - `{r.Name}` by {r.Author} - **{r.Songs.Count}** songs"))).ConfigureAwait(false);
} }
//todo only author or owner //todo only author or owner
@ -634,9 +741,9 @@ namespace NadekoBot.Modules.Music
} }
if (!success) if (!success)
await channel.SendMessageAsync("Failed to delete that playlist. It either doesn't exist, or you are not its author.").ConfigureAwait(false); await channel.SendErrorAsync("Failed to delete that playlist. It either doesn't exist, or you are not its author.").ConfigureAwait(false);
else else
await channel.SendMessageAsync("`Playlist successfully deleted.`").ConfigureAwait(false); await channel.SendConfirmAsync("🗑 Playlist successfully **deleted**.").ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -678,42 +785,7 @@ namespace NadekoBot.Modules.Music
if (seconds.Length == 1) if (seconds.Length == 1)
seconds = "0" + seconds; seconds = "0" + seconds;
await channel.SendMessageAsync($"`Skipped to {minutes}:{seconds}`").ConfigureAwait(false); await channel.SendConfirmAsync($"Skipped to `{minutes}:{seconds}`").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task GetLink(IUserMessage umsg, int index = 0)
{
var channel = (ITextChannel)umsg.Channel;
MusicPlayer musicPlayer;
if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer))
return;
if (index < 0)
return;
if (index > 0)
{
var selSong = musicPlayer.Playlist.DefaultIfEmpty(null).ElementAtOrDefault(index - 1);
if (selSong == null)
{
await channel.SendMessageAsync("Could not select song, likely wrong index");
}
else
{
await channel.SendMessageAsync($"🎶`Selected song {selSong.SongInfo.Title}:` <{selSong.SongInfo.Query}>").ConfigureAwait(false);
}
}
else
{
var curSong = musicPlayer.CurrentSong;
if (curSong == null)
return;
await channel.SendMessageAsync($"🎶`Current song:` <{curSong.SongInfo.Query}>").ConfigureAwait(false);
}
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -726,9 +798,9 @@ namespace NadekoBot.Modules.Music
return; return;
if (!musicPlayer.ToggleAutoplay()) if (!musicPlayer.ToggleAutoplay())
await channel.SendMessageAsync("🎶`Autoplay disabled.`").ConfigureAwait(false); await channel.SendConfirmAsync("❌ Autoplay disabled.").ConfigureAwait(false);
else else
await channel.SendMessageAsync("🎶`Autoplay enabled.`").ConfigureAwait(false); await channel.SendConfirmAsync("✅ Autoplay enabled.").ConfigureAwait(false);
} }
public static async Task QueueSong(IGuildUser queuer, ITextChannel textCh, IVoiceChannel voiceCh, string query, bool silent = false, MusicType musicType = MusicType.Normal) public static async Task QueueSong(IGuildUser queuer, ITextChannel textCh, IVoiceChannel voiceCh, string query, bool silent = false, MusicType musicType = MusicType.Normal)
@ -736,7 +808,7 @@ namespace NadekoBot.Modules.Music
if (voiceCh == null || voiceCh.Guild != textCh.Guild) if (voiceCh == null || voiceCh.Guild != textCh.Guild)
{ {
if (!silent) if (!silent)
await textCh.SendMessageAsync("💢 You need to be in a voice channel on this server.\n If you are already in a voice channel, try rejoining.").ConfigureAwait(false); await textCh.SendErrorAsync("💢 You need to be in a voice channel on this server.\n If you are already in a voice channel, try rejoining.").ConfigureAwait(false);
throw new ArgumentNullException(nameof(voiceCh)); throw new ArgumentNullException(nameof(voiceCh));
} }
if (string.IsNullOrWhiteSpace(query) || query.Length < 3) if (string.IsNullOrWhiteSpace(query) || query.Length < 3)
@ -747,43 +819,72 @@ namespace NadekoBot.Modules.Music
float vol = 1;// SpecificConfigurations.Default.Of(server.Id).DefaultMusicVolume; float vol = 1;// SpecificConfigurations.Default.Of(server.Id).DefaultMusicVolume;
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
vol = uow.GuildConfigs.For(textCh.Guild.Id).DefaultMusicVolume; vol = uow.GuildConfigs.For(textCh.Guild.Id, set => set).DefaultMusicVolume;
} }
var mp = new MusicPlayer(voiceCh, vol); var mp = new MusicPlayer(voiceCh, vol);
IUserMessage finishedMessage = null;
IUserMessage playingMessage = null;
IUserMessage lastFinishedMessage = null;
mp.OnCompleted += async (s, song) => mp.OnCompleted += async (s, song) =>
{
if (song.PrintStatusMessage)
{ {
try try
{ {
if (lastFinishedMessage != null) if (finishedMessage != null)
await lastFinishedMessage.DeleteAsync().ConfigureAwait(false); finishedMessage.DeleteAfter(0);
if (playingMessage != null)
await playingMessage.DeleteAsync().ConfigureAwait(false); finishedMessage = await textCh.EmbedAsync(new EmbedBuilder().WithOkColor()
try { lastFinishedMessage = await textCh.SendMessageAsync($"🎵`Finished`{song.PrettyName}").ConfigureAwait(false); } catch { } .WithAuthor(eab => eab.WithName("Finished Song").WithMusicIcon())
.WithDescription(song.PrettyName)
.WithFooter(ef => ef.WithText(song.PrettyInfo))
.Build())
.ConfigureAwait(false);
if (mp.Autoplay && mp.Playlist.Count == 0 && song.SongInfo.Provider == "YouTube") if (mp.Autoplay && mp.Playlist.Count == 0 && song.SongInfo.Provider == "YouTube")
{ {
await QueueSong(queuer.Guild.GetCurrentUser(), textCh, voiceCh, (await NadekoBot.Google.GetRelatedVideosAsync(song.SongInfo.Query, 4)).ToList().Shuffle().FirstOrDefault(), silent, musicType).ConfigureAwait(false); await QueueSong(queuer.Guild.GetCurrentUser(), textCh, voiceCh, (await NadekoBot.Google.GetRelatedVideosAsync(song.SongInfo.Query, 4)).ToList().Shuffle().FirstOrDefault(), silent, musicType).ConfigureAwait(false);
} }
} }
catch { } catch { }
}
}; };
mp.OnStarted += async (s, song) => IUserMessage playingMessage = null;
mp.OnStarted += async (player, song) =>
{ {
if (song.PrintStatusMessage) try { await mp.UpdateSongDurationsAsync().ConfigureAwait(false); } catch { }
{ var sender = player as MusicPlayer;
var sender = s as MusicPlayer;
if (sender == null) if (sender == null)
return; return;
try
{
if (playingMessage != null)
playingMessage.DeleteAfter(0);
var msgTxt = $"🎵`Playing`{song.PrettyName} `Vol: {(int)(sender.Volume * 100)}%`"; playingMessage = await textCh.EmbedAsync(new EmbedBuilder().WithOkColor()
try { playingMessage = await textCh.SendMessageAsync(msgTxt).ConfigureAwait(false); } catch { } .WithAuthor(eab => eab.WithName("Playing Song").WithMusicIcon())
.WithDescription(song.PrettyName)
.WithFooter(ef => ef.WithText(song.PrettyInfo))
.Build())
.ConfigureAwait(false);
} }
catch { }
};
mp.OnPauseChanged += async (paused) =>
{
try
{
IUserMessage pauseMessage = null;
if (paused)
{
pauseMessage = await textCh.SendConfirmAsync("🎵 Music playback **paused**.").ConfigureAwait(false);
}
else
{
pauseMessage = await textCh.SendConfirmAsync("🎵 Music playback **resumed**.").ConfigureAwait(false);
}
if (pauseMessage != null)
pauseMessage.DeleteAfter(15);
}
catch { }
}; };
return mp; return mp;
}); });
@ -791,7 +892,7 @@ namespace NadekoBot.Modules.Music
try try
{ {
musicPlayer.ThrowIfQueueFull(); musicPlayer.ThrowIfQueueFull();
resolvedSong = await Song.ResolveSong(query, musicType).ConfigureAwait(false); resolvedSong = await SongHandler.ResolveSong(query, musicType).ConfigureAwait(false);
if (resolvedSong == null) if (resolvedSong == null)
throw new SongNotFoundException(); throw new SongNotFoundException();
@ -800,24 +901,23 @@ namespace NadekoBot.Modules.Music
} }
catch (PlaylistFullException) catch (PlaylistFullException)
{ {
try { await textCh.SendMessageAsync($"🎵 `Queue is full at {musicPlayer.MaxQueueSize}/{musicPlayer.MaxQueueSize}.` "); } catch { } try { await textCh.SendConfirmAsync($"🎵 Queue is full at **{musicPlayer.MaxQueueSize}/{musicPlayer.MaxQueueSize}**."); } catch { }
throw; throw;
} }
if (!silent) if (!silent)
{ {
try try
{ {
var queuedMessage = await textCh.SendMessageAsync($"🎵`Queued`{resolvedSong.PrettyName} **at** `#{musicPlayer.Playlist.Count + 1}`").ConfigureAwait(false); //var queuedMessage = await textCh.SendConfirmAsync($"🎵 Queued **{resolvedSong.SongInfo.Title}** at `#{musicPlayer.Playlist.Count + 1}`").ConfigureAwait(false);
var t = Task.Run(async () => var queuedMessage = await textCh.EmbedAsync(new EmbedBuilder().WithOkColor()
{ .WithAuthor(eab => eab.WithName("Queued Song #" + (musicPlayer.Playlist.Count + 1)).WithMusicIcon())
try .WithDescription($"{resolvedSong.PrettyName}\nQueue ")
{ .WithThumbnail(tn => tn.Url = resolvedSong.Thumbnail)
await Task.Delay(10000).ConfigureAwait(false); .WithFooter(ef => ef.WithText(resolvedSong.PrettyProvider))
.Build())
await queuedMessage.DeleteAsync().ConfigureAwait(false); .ConfigureAwait(false);
} if (queuedMessage != null)
catch { } queuedMessage.DeleteAfter(10);
}).ConfigureAwait(false);
} }
catch { } // if queued message sending fails, don't attempt to delete it catch { } // if queued message sending fails, don't attempt to delete it
} }

View File

@ -10,13 +10,14 @@ using System.Net.Http;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Xml.Linq; using System.Xml.Linq;
using NadekoBot.Extensions; using NadekoBot.Extensions;
using System.Xml;
namespace NadekoBot.Modules.NSFW namespace NadekoBot.Modules.NSFW
{ {
[NadekoModule("NSFW", "~")] [NadekoModule("NSFW", "~")]
public class NSFW : DiscordModule public class NSFW : DiscordModule
{ {
public NSFW(ILocalization loc, CommandService cmds, ShardedDiscordClient client) : base(loc, cmds, client) public NSFW() : base()
{ {
} }
@ -51,11 +52,12 @@ namespace NadekoBot.Modules.NSFW
} }
var link = await provider.ConfigureAwait(false); var link = await provider.ConfigureAwait(false);
if (string.IsNullOrWhiteSpace(link)) if (string.IsNullOrWhiteSpace(link))
await channel.SendMessageAsync("Search yielded no results ;(").ConfigureAwait(false); await channel.SendErrorAsync("No results found.").ConfigureAwait(false);
else else
await channel.SendMessageAsync(link).ConfigureAwait(false); await channel.SendMessageAsync(link).ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task HentaiBomb(IUserMessage umsg, [Remainder] string tag = null) public async Task HentaiBomb(IUserMessage umsg, [Remainder] string tag = null)
@ -72,45 +74,13 @@ namespace NadekoBot.Modules.NSFW
if (links.All(l => l == null)) if (links.All(l => l == null))
{ {
await channel.SendMessageAsync("`No results.`").ConfigureAwait(false); await channel.SendErrorAsync("No results found.").ConfigureAwait(false);
return; return;
} }
await channel.SendMessageAsync(String.Join("\n\n", links)).ConfigureAwait(false); await channel.SendMessageAsync(String.Join("\n\n", links)).ConfigureAwait(false);
} }
public static async Task<string> GetYandereImageLink(string tag)
{
var rng = new NadekoRandom();
var url =
$"https://yande.re/post.xml?" +
$"limit=25" +
$"&page={rng.Next(0, 15)}" +
$"&tags={tag.Replace(" ", "_")}";
using (var http = new HttpClient())
{
var webpage = await http.GetStringAsync(url).ConfigureAwait(false);
var matches = Regex.Matches(webpage, "file_url=\"(?<url>.*?)\"");
//var rating = Regex.Matches(webpage, "rating=\"(?<rate>.*?)\"");
if (matches.Count == 0)
return null;
return matches[rng.Next(0, matches.Count)].Groups["url"].Value;
}
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Yandere(IUserMessage umsg, [Remainder] string tag = null)
{
var channel = (ITextChannel)umsg.Channel;
tag = tag?.Trim() ?? "";
var link = await GetYandereImageLink(tag).ConfigureAwait(false);
if (string.IsNullOrWhiteSpace(link))
await channel.SendMessageAsync("Search yielded no results ;(").ConfigureAwait(false);
else
await channel.SendMessageAsync(link).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
@ -119,54 +89,38 @@ namespace NadekoBot.Modules.NSFW
var channel = (ITextChannel)umsg.Channel; var channel = (ITextChannel)umsg.Channel;
tag = tag?.Trim() ?? ""; tag = tag?.Trim() ?? "";
var link = await GetDanbooruImageLink(tag).ConfigureAwait(false);
if (string.IsNullOrWhiteSpace(link)) var url = await GetDanbooruImageLink(tag).ConfigureAwait(false);
await channel.SendMessageAsync("Search yielded no results ;(").ConfigureAwait(false);
if (url == null)
await channel.SendErrorAsync(umsg.Author.Mention + " No results.");
else else
await channel.SendMessageAsync(link).ConfigureAwait(false); await channel.EmbedAsync(new EmbedBuilder().WithOkColor()
.WithDescription(umsg.Author.Mention + " " + tag)
.WithImageUrl(url)
.WithFooter(efb => efb.WithText("Danbooru"))
.Build()).ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task Konachan(IUserMessage umsg, [Remainder] string tag = null) public Task Yandere(IUserMessage umsg, [Remainder] string tag = null)
{ => Searches.Searches.InternalDapiCommand(umsg, tag, Searches.Searches.DapiSearchType.Yandere);
var channel = (ITextChannel)umsg.Channel;
tag = tag?.Trim() ?? "";
var link = await GetKonachanImageLink(tag).ConfigureAwait(false);
if (string.IsNullOrWhiteSpace(link))
await channel.SendMessageAsync("Search yielded no results ;(").ConfigureAwait(false);
else
await channel.SendMessageAsync(link).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task Gelbooru(IUserMessage umsg, [Remainder] string tag = null) public Task Konachan(IUserMessage umsg, [Remainder] string tag = null)
{ => Searches.Searches.InternalDapiCommand(umsg, tag, Searches.Searches.DapiSearchType.Konachan);
var channel = (ITextChannel)umsg.Channel;
tag = tag?.Trim() ?? "";
var link = await GetGelbooruImageLink(tag).ConfigureAwait(false);
if (string.IsNullOrWhiteSpace(link))
await channel.SendMessageAsync("Search yielded no results ;(").ConfigureAwait(false);
else
await channel.SendMessageAsync(link).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task Rule34(IUserMessage umsg, [Remainder] string tag = null) public Task Gelbooru(IUserMessage umsg, [Remainder] string tag = null)
{ => Searches.Searches.InternalDapiCommand(umsg, tag, Searches.Searches.DapiSearchType.Gelbooru);
var channel = (ITextChannel)umsg.Channel;
tag = tag?.Trim() ?? ""; [NadekoCommand, Usage, Description, Aliases]
var link = await GetRule34ImageLink(tag).ConfigureAwait(false); [RequireContext(ContextType.Guild)]
if (string.IsNullOrWhiteSpace(link)) public Task Rule34(IUserMessage umsg, [Remainder] string tag = null)
await channel.SendMessageAsync("Search yielded no results ;(").ConfigureAwait(false); => Searches.Searches.InternalDapiCommand(umsg, tag, Searches.Searches.DapiSearchType.Rule34);
else
await channel.SendMessageAsync(link).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
@ -175,11 +129,17 @@ namespace NadekoBot.Modules.NSFW
var channel = (ITextChannel)umsg.Channel; var channel = (ITextChannel)umsg.Channel;
tag = tag?.Trim() ?? ""; tag = tag?.Trim() ?? "";
var link = await GetE621ImageLink(tag).ConfigureAwait(false);
if (string.IsNullOrWhiteSpace(link)) var url = await GetE621ImageLink(tag).ConfigureAwait(false);
await channel.SendMessageAsync("Search yielded no results ;(").ConfigureAwait(false);
if (url == null)
await channel.SendErrorAsync(umsg.Author.Mention + " No results.");
else else
await channel.SendMessageAsync(link).ConfigureAwait(false); await channel.EmbedAsync(new EmbedBuilder().WithOkColor()
.WithDescription(umsg.Author.Mention + " " + tag)
.WithImageUrl(url)
.WithFooter(efb => efb.WithText("e621"))
.Build()).ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -201,13 +161,13 @@ namespace NadekoBot.Modules.NSFW
JToken obj; JToken obj;
using (var http = new HttpClient()) using (var http = new HttpClient())
{ {
obj = JArray.Parse(await http.GetStringAsync($"http://api.oboobs.ru/boobs/{ new NadekoRandom().Next(0, 9880) }").ConfigureAwait(false))[0]; obj = JArray.Parse(await http.GetStringAsync($"http://api.oboobs.ru/boobs/{ new NadekoRandom().Next(0, 10229) }").ConfigureAwait(false))[0];
} }
await channel.SendMessageAsync($"http://media.oboobs.ru/{ obj["preview"].ToString() }").ConfigureAwait(false); await channel.SendMessageAsync($"http://media.oboobs.ru/{ obj["preview"].ToString() }").ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {
await channel.SendMessageAsync($"💢 {ex.Message}").ConfigureAwait(false); await channel.SendErrorAsync(ex.Message).ConfigureAwait(false);
} }
} }
@ -222,109 +182,71 @@ namespace NadekoBot.Modules.NSFW
JToken obj; JToken obj;
using (var http = new HttpClient()) using (var http = new HttpClient())
{ {
obj = JArray.Parse(await http.GetStringAsync($"http://api.obutts.ru/butts/{ new NadekoRandom().Next(0, 3873) }").ConfigureAwait(false))[0]; obj = JArray.Parse(await http.GetStringAsync($"http://api.obutts.ru/butts/{ new NadekoRandom().Next(0, 4222) }").ConfigureAwait(false))[0];
} }
await channel.SendMessageAsync($"http://media.obutts.ru/{ obj["preview"].ToString() }").ConfigureAwait(false); await channel.SendMessageAsync($"http://media.obutts.ru/{ obj["preview"].ToString() }").ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {
await channel.SendMessageAsync($"💢 {ex.Message}").ConfigureAwait(false); await channel.SendErrorAsync(ex.Message).ConfigureAwait(false);
}
}
public static async Task<string> GetKonachanImageLink(string tag)
{
var rng = new NadekoRandom();
var link = $"http://konachan.com/post?" +
$"page={rng.Next(0, 5)}";
if (!string.IsNullOrWhiteSpace(tag))
link += $"&tags={tag.Replace(" ", "_")}";
using (var http = new HttpClient())
{
var webpage = await http.GetStringAsync(link).ConfigureAwait(false);
var matches = Regex.Matches(webpage, "<a class=\"directlink largeimg\" href=\"(?<ll>.*?)\">");
if (matches.Count == 0)
return null;
return matches[rng.Next(0, matches.Count)].Groups["ll"].Value;
} }
} }
public static async Task<string> GetDanbooruImageLink(string tag) public static async Task<string> GetDanbooruImageLink(string tag)
{
var rng = new NadekoRandom();
if (tag == "loli") //loli doesn't work for some reason atm
tag = "flat_chest";
var link = $"http://danbooru.donmai.us/posts?" +
$"page={rng.Next(0, 15)}";
if (!string.IsNullOrWhiteSpace(tag))
link += $"&tags={tag.Replace(" ", "_")}";
using (var http = new HttpClient())
{
var webpage = await http.GetStringAsync(link).ConfigureAwait(false);
var matches = Regex.Matches(webpage, "data-large-file-url=\"(?<id>.*?)\"");
if (matches.Count == 0)
return null;
return $"http://danbooru.donmai.us" +
$"{matches[rng.Next(0, matches.Count)].Groups["id"].Value}";
}
}
public static async Task<string> GetGelbooruImageLink(string tag)
{
using (var http = new HttpClient())
{
http.AddFakeHeaders();
var webpage = await http.GetStringAsync("http://gelbooru.com/index.php?page=dapi&s=post&q=index&limit=100&tags="+ tag.Replace(" ", "_")).ConfigureAwait(false);
var matches = Regex.Matches(webpage, "file_url=\"(?<url>.*?)\"");
if (matches.Count == 0)
return null;
var rng = new NadekoRandom();
var match = matches[rng.Next(0, matches.Count)];
return matches[rng.Next(0, matches.Count)].Groups["url"].Value;
}
}
public static async Task<string> GetRule34ImageLink(string tag)
{
var rng = new NadekoRandom();
var url =
$"http://rule34.xxx/index.php?page=dapi&s=post&q=index&limit=100&tags={tag.Replace(" ", "_")}";
using (var http = new HttpClient())
{
var webpage = await http.GetStringAsync(url).ConfigureAwait(false);
var matches = Regex.Matches(webpage, "file_url=\"(?<url>.*?)\"");
if (matches.Count == 0)
return null;
var match = matches[rng.Next(0, matches.Count)];
return "http:" + matches[rng.Next(0, matches.Count)].Groups["url"].Value;
}
}
public static async Task<string> GetE621ImageLink(string tags)
{ {
try try
{ {
using (var http = new HttpClient()) using (var http = new HttpClient())
{ {
http.AddFakeHeaders(); http.AddFakeHeaders();
var data = await http.GetStreamAsync("http://e621.net/post/index.xml?tags=" + Uri.EscapeUriString(tags) + "%20order:random&limit=1"); var data = await http.GetStreamAsync("https://danbooru.donmai.us/posts.xml?limit=100&tags=" + tag);
var doc = XDocument.Load(data); var doc = new XmlDocument();
return doc.Descendants("file_url").FirstOrDefault().Value; doc.Load(data);
var nodes = doc.GetElementsByTagName("file-url");
var node = nodes[new NadekoRandom().Next(0, nodes.Count)];
return "https://danbooru.donmai.us" + node.InnerText;
} }
} }
catch (Exception ex) catch
{ {
Console.WriteLine("Error in e621 search: \n" + ex); return null;
return "Error, do you have too many tags?";
} }
} }
public static async Task<string> GetE621ImageLink(string tag)
{
try
{
using (var http = new HttpClient())
{
http.AddFakeHeaders();
var data = await http.GetStreamAsync("http://e621.net/post/index.xml?tags=" + tag);
var doc = new XmlDocument();
doc.Load(data);
var nodes = doc.GetElementsByTagName("file_url");
var node = nodes[new NadekoRandom().Next(0, nodes.Count)];
return node.InnerText;
}
}
catch
{
return null;
}
}
public static Task<string> GetYandereImageLink(string tag) =>
Searches.Searches.InternalDapiSearch(tag, Searches.Searches.DapiSearchType.Yandere);
public static Task<string> GetKonachanImageLink(string tag) =>
Searches.Searches.InternalDapiSearch(tag, Searches.Searches.DapiSearchType.Konachan);
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);
} }
} }

View File

@ -1,6 +1,7 @@
using Discord; using Discord;
using Discord.Commands; using Discord.Commands;
using NadekoBot.Attributes; using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Modules.Games.Trivia; using NadekoBot.Modules.Games.Trivia;
using NadekoBot.Services; using NadekoBot.Services;
using NadekoBot.Services.Database.Models; using NadekoBot.Services.Database.Models;
@ -104,7 +105,10 @@ namespace NadekoBot.Modules.Permissions
} }
await channel.SendMessageAsync(":ok:").ConfigureAwait(false); if(action == AddRemove.Add)
await channel.SendConfirmAsync($"Blacklisted a `{type}` with id `{id}`").ConfigureAwait(false);
else
await channel.SendConfirmAsync($"Unblacklisted a `{type}` with id `{id}`").ConfigureAwait(false);
} }
} }
} }

View File

@ -1,5 +1,6 @@
using Discord; using Discord;
using Discord.Commands; using Discord.Commands;
using Microsoft.EntityFrameworkCore;
using NadekoBot.Attributes; using NadekoBot.Attributes;
using NadekoBot.Extensions; using NadekoBot.Extensions;
using NadekoBot.Services; using NadekoBot.Services;
@ -23,7 +24,7 @@ namespace NadekoBot.Modules.Permissions
public class CmdCdsCommands public class CmdCdsCommands
{ {
public static ConcurrentDictionary<ulong, ConcurrentHashSet<CommandCooldown>> commandCooldowns { get; } public static ConcurrentDictionary<ulong, ConcurrentHashSet<CommandCooldown>> commandCooldowns { get; }
private static ConcurrentDictionary<ulong, ConcurrentHashSet<ActiveCooldown>> activeCooldowns = new ConcurrentDictionary<ulong, ConcurrentHashSet<ActiveCooldown>>(); private static ConcurrentDictionary<ulong, ConcurrentHashSet<ActiveCooldown>> activeCooldowns { get; } = new ConcurrentDictionary<ulong, ConcurrentHashSet<ActiveCooldown>>();
static CmdCdsCommands() static CmdCdsCommands()
{ {
@ -40,13 +41,13 @@ namespace NadekoBot.Modules.Permissions
var channel = (ITextChannel)imsg.Channel; var channel = (ITextChannel)imsg.Channel;
if (secs < 0 || secs > 3600) if (secs < 0 || secs > 3600)
{ {
await channel.SendMessageAsync("⚠️ Invalid second parameter. (Must be a number between 0 and 3600)").ConfigureAwait(false); await channel.SendErrorAsync("Invalid second parameter. (Must be a number between 0 and 3600)").ConfigureAwait(false);
return; return;
} }
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
var config = uow.GuildConfigs.For(channel.Guild.Id); 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.Text.ToLowerInvariant()); config.CommandCooldowns.RemoveWhere(cc => cc.CommandName == command.Text.ToLowerInvariant());
@ -67,10 +68,14 @@ namespace NadekoBot.Modules.Permissions
{ {
var activeCds = activeCooldowns.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet<ActiveCooldown>()); var activeCds = activeCooldowns.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet<ActiveCooldown>());
activeCds.RemoveWhere(ac => ac.Command == command.Text.ToLowerInvariant()); activeCds.RemoveWhere(ac => ac.Command == command.Text.ToLowerInvariant());
await channel.SendMessageAsync($"🚮 Command **{command}** has no coooldown now and all existing cooldowns have been cleared.").ConfigureAwait(false); await channel.SendConfirmAsync($"🚮 Command **{command}** has no coooldown now and all existing cooldowns have been cleared.")
.ConfigureAwait(false);
} }
else else
await channel.SendMessageAsync($"✅ Command **{command}** now has a **{secs} {(secs == 1 ? "second" : "seconds")}** cooldown.").ConfigureAwait(false); {
await channel.SendConfirmAsync($"✅ Command **{command}** now has a **{secs} {"seconds".SnPl(secs)}** cooldown.")
.ConfigureAwait(false);
}
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -81,7 +86,7 @@ namespace NadekoBot.Modules.Permissions
var localSet = commandCooldowns.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet<CommandCooldown>()); var localSet = commandCooldowns.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet<CommandCooldown>());
if (!localSet.Any()) if (!localSet.Any())
await channel.SendMessageAsync(" `No command cooldowns set.`").ConfigureAwait(false); await channel.SendConfirmAsync(" `No command cooldowns set.`").ConfigureAwait(false);
else 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 + " secs"), s => $"{s,-30}", 2).ConfigureAwait(false);
} }

View File

@ -1,6 +1,8 @@
using Discord; using Discord;
using Discord.Commands; using Discord.Commands;
using Microsoft.EntityFrameworkCore;
using NadekoBot.Attributes; using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Services; using NadekoBot.Services;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Linq; using System.Linq;
@ -13,14 +15,14 @@ namespace NadekoBot.Modules.Permissions
[Group] [Group]
public class FilterCommands public class FilterCommands
{ {
public static ConcurrentHashSet<ulong> InviteFilteringChannels { get; set; } public static ConcurrentHashSet<ulong> InviteFilteringChannels { get; }
public static ConcurrentHashSet<ulong> InviteFilteringServers { get; set; } public static ConcurrentHashSet<ulong> InviteFilteringServers { get; }
//serverid, filteredwords //serverid, filteredwords
private static ConcurrentDictionary<ulong, ConcurrentHashSet<string>> ServerFilteredWords { get; set; } private static ConcurrentDictionary<ulong, ConcurrentHashSet<string>> ServerFilteredWords { get; }
public static ConcurrentHashSet<ulong> WordFilteringChannels { get; set; } public static ConcurrentHashSet<ulong> WordFilteringChannels { get; }
public static ConcurrentHashSet<ulong> WordFilteringServers { get; set; } public static ConcurrentHashSet<ulong> WordFilteringServers { get; }
public static ConcurrentHashSet<string> FilteredWordsForChannel(ulong channelId, ulong guildId) public static ConcurrentHashSet<string> FilteredWordsForChannel(ulong channelId, ulong guildId)
{ {
@ -68,7 +70,7 @@ namespace NadekoBot.Modules.Permissions
bool enabled; bool enabled;
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
var config = uow.GuildConfigs.For(channel.Guild.Id); var config = uow.GuildConfigs.For(channel.Guild.Id, set => set);
enabled = config.FilterInvites = !config.FilterInvites; enabled = config.FilterInvites = !config.FilterInvites;
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
} }
@ -76,12 +78,12 @@ namespace NadekoBot.Modules.Permissions
if (enabled) if (enabled)
{ {
InviteFilteringServers.Add(channel.Guild.Id); InviteFilteringServers.Add(channel.Guild.Id);
await channel.SendMessageAsync("✅ `Invite filtering enabled on this server.`").ConfigureAwait(false); await channel.SendConfirmAsync("Invite filtering enabled on this server.").ConfigureAwait(false);
} }
else else
{ {
InviteFilteringServers.TryRemove(channel.Guild.Id); InviteFilteringServers.TryRemove(channel.Guild.Id);
await channel.SendMessageAsync(" `Invite filtering disabled on this server.`").ConfigureAwait(false); await channel.SendConfirmAsync("Invite filtering disabled on this server.").ConfigureAwait(false);
} }
} }
@ -94,7 +96,7 @@ namespace NadekoBot.Modules.Permissions
int removed; int removed;
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
var config = uow.GuildConfigs.For(channel.Guild.Id); var config = uow.GuildConfigs.For(channel.Guild.Id, set => set.Include(gc => gc.FilterInvitesChannelIds));
removed = config.FilterInvitesChannelIds.RemoveWhere(fc => fc.ChannelId == channel.Id); removed = config.FilterInvitesChannelIds.RemoveWhere(fc => fc.ChannelId == channel.Id);
if (removed == 0) if (removed == 0)
{ {
@ -109,12 +111,12 @@ namespace NadekoBot.Modules.Permissions
if (removed == 0) if (removed == 0)
{ {
InviteFilteringChannels.Add(channel.Id); InviteFilteringChannels.Add(channel.Id);
await channel.SendMessageAsync("✅ `Invite filtering enabled on this channel.`").ConfigureAwait(false); await channel.SendConfirmAsync("Invite filtering enabled on this channel.").ConfigureAwait(false);
} }
else else
{ {
InviteFilteringChannels.TryRemove(channel.Id); InviteFilteringChannels.TryRemove(channel.Id);
await channel.SendMessageAsync(" `Invite filtering disabled on this channel.`").ConfigureAwait(false); await channel.SendConfirmAsync("Invite filtering disabled on this channel.").ConfigureAwait(false);
} }
} }
@ -127,7 +129,7 @@ namespace NadekoBot.Modules.Permissions
bool enabled; bool enabled;
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
var config = uow.GuildConfigs.For(channel.Guild.Id); var config = uow.GuildConfigs.For(channel.Guild.Id, set => set);
enabled = config.FilterWords = !config.FilterWords; enabled = config.FilterWords = !config.FilterWords;
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
} }
@ -135,12 +137,12 @@ namespace NadekoBot.Modules.Permissions
if (enabled) if (enabled)
{ {
WordFilteringServers.Add(channel.Guild.Id); WordFilteringServers.Add(channel.Guild.Id);
await channel.SendMessageAsync("✅ `Word filtering enabled on this server.`").ConfigureAwait(false); await channel.SendConfirmAsync("Word filtering enabled on this server.").ConfigureAwait(false);
} }
else else
{ {
WordFilteringServers.TryRemove(channel.Guild.Id); WordFilteringServers.TryRemove(channel.Guild.Id);
await channel.SendMessageAsync(" `Word filtering disabled on this server.`").ConfigureAwait(false); await channel.SendConfirmAsync("Word filtering disabled on this server.").ConfigureAwait(false);
} }
} }
@ -153,7 +155,7 @@ namespace NadekoBot.Modules.Permissions
int removed; int removed;
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
var config = uow.GuildConfigs.For(channel.Guild.Id); var config = uow.GuildConfigs.For(channel.Guild.Id, set => set.Include(gc => gc.FilterWordsChannelIds));
removed = config.FilterWordsChannelIds.RemoveWhere(fc => fc.ChannelId == channel.Id); removed = config.FilterWordsChannelIds.RemoveWhere(fc => fc.ChannelId == channel.Id);
if (removed == 0) if (removed == 0)
{ {
@ -168,12 +170,12 @@ namespace NadekoBot.Modules.Permissions
if (removed == 0) if (removed == 0)
{ {
WordFilteringChannels.Add(channel.Id); WordFilteringChannels.Add(channel.Id);
await channel.SendMessageAsync("✅ `Word filtering enabled on this channel.`").ConfigureAwait(false); await channel.SendConfirmAsync("Word filtering enabled on this channel.").ConfigureAwait(false);
} }
else else
{ {
WordFilteringChannels.TryRemove(channel.Id); WordFilteringChannels.TryRemove(channel.Id);
await channel.SendMessageAsync(" `Word filtering disabled on this channel.`").ConfigureAwait(false); await channel.SendConfirmAsync("Word filtering disabled on this channel.").ConfigureAwait(false);
} }
} }
@ -191,7 +193,7 @@ namespace NadekoBot.Modules.Permissions
int removed; int removed;
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
var config = uow.GuildConfigs.For(channel.Guild.Id); var config = uow.GuildConfigs.For(channel.Guild.Id, set => set.Include(gc => gc.FilteredWords));
removed = config.FilteredWords.RemoveWhere(fw => fw.Word == word); removed = config.FilteredWords.RemoveWhere(fw => fw.Word == word);
@ -206,13 +208,13 @@ namespace NadekoBot.Modules.Permissions
if (removed == 0) if (removed == 0)
{ {
filteredWords.Add(word); filteredWords.Add(word);
await channel.SendMessageAsync($"✅ Word `{word}` successfully added to the list of filtered words.") await channel.SendConfirmAsync($"Word `{word}` successfully added to the list of filtered words.")
.ConfigureAwait(false); .ConfigureAwait(false);
} }
else else
{ {
filteredWords.TryRemove(word); filteredWords.TryRemove(word);
await channel.SendMessageAsync($" Word `{word}` removed from the list of filtered words.") await channel.SendConfirmAsync($"Word `{word}` removed from the list of filtered words.")
.ConfigureAwait(false); .ConfigureAwait(false);
} }
} }
@ -226,7 +228,7 @@ namespace NadekoBot.Modules.Permissions
ConcurrentHashSet<string> filteredWords; ConcurrentHashSet<string> filteredWords;
ServerFilteredWords.TryGetValue(channel.Guild.Id, out filteredWords); ServerFilteredWords.TryGetValue(channel.Guild.Id, out filteredWords);
await channel.SendMessageAsync($" `List of banned words:`\n" + string.Join(",\n", filteredWords)) await channel.SendConfirmAsync($"List of filtered words", string.Join("\n", filteredWords))
.ConfigureAwait(false); .ConfigureAwait(false);
} }
} }

View File

@ -7,6 +7,7 @@ using NadekoBot.Services;
using Discord; using Discord;
using NadekoBot.Services.Database.Models; using NadekoBot.Services.Database.Models;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using NadekoBot.Extensions;
namespace NadekoBot.Modules.Permissions namespace NadekoBot.Modules.Permissions
{ {
@ -21,7 +22,7 @@ namespace NadekoBot.Modules.Permissions
} }
//guildid, root permission //guildid, root permission
public static ConcurrentDictionary<ulong, PermissionCache> Cache; public static ConcurrentDictionary<ulong, PermissionCache> Cache { get; }
static Permissions() static Permissions()
{ {
@ -39,7 +40,7 @@ namespace NadekoBot.Modules.Permissions
} }
} }
public Permissions(ILocalization loc, CommandService cmds, ShardedDiscordClient client) : base(loc, cmds, client) public Permissions() : base()
{ {
} }
@ -51,7 +52,7 @@ namespace NadekoBot.Modules.Permissions
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
var config = uow.GuildConfigs.For(channel.Guild.Id); var config = uow.GuildConfigs.For(channel.Guild.Id, set => set);
config.VerbosePermissions = action.Value; config.VerbosePermissions = action.Value;
Cache.AddOrUpdate(channel.Guild.Id, new PermissionCache() Cache.AddOrUpdate(channel.Guild.Id, new PermissionCache()
{ {
@ -62,7 +63,7 @@ namespace NadekoBot.Modules.Permissions
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
} }
await channel.SendMessageAsync(" I will " + (action.Value ? "now" : "no longer") + " show permission warnings.").ConfigureAwait(false); await channel.SendConfirmAsync(" I will " + (action.Value ? "now" : "no longer") + " show permission warnings.").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -72,10 +73,10 @@ namespace NadekoBot.Modules.Permissions
var channel = (ITextChannel)msg.Channel; var channel = (ITextChannel)msg.Channel;
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
var config = uow.GuildConfigs.For(channel.Guild.Id); var config = uow.GuildConfigs.For(channel.Guild.Id, set => set);
if (role == null) if (role == null)
{ {
await channel.SendMessageAsync($" Current permission role is **{config.PermissionRole}**.").ConfigureAwait(false); await channel.SendConfirmAsync($" Current permission role is **{config.PermissionRole}**.").ConfigureAwait(false);
return; return;
} }
else { else {
@ -90,7 +91,7 @@ namespace NadekoBot.Modules.Permissions
} }
} }
await channel.SendMessageAsync($"✅ Users now require **{role.Name}** role in order to edit permissions.").ConfigureAwait(false); await channel.SendConfirmAsync($"Users now require **{role.Name}** role in order to edit permissions.").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -109,9 +110,6 @@ namespace NadekoBot.Modules.Permissions
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(channel.Guild) + " [uneditable]") : (p.GetCommand(channel.Guild)))}")); 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(channel.Guild) + " [uneditable]") : (p.GetCommand(channel.Guild)))}"));
} }
if (string.IsNullOrWhiteSpace(toSend))
await channel.SendMessageAsync("❗️`No permissions set.`").ConfigureAwait(false);
else
await channel.SendMessageAsync(toSend).ConfigureAwait(false); await channel.SendMessageAsync(toSend).ConfigureAwait(false);
} }
@ -156,11 +154,11 @@ namespace NadekoBot.Modules.Permissions
uow2._context.SaveChanges(); uow2._context.SaveChanges();
} }
await channel.SendMessageAsync($"✅ {imsg.Author.Mention} removed permission **{p.GetCommand(channel.Guild)}** from position #{index + 1}.").ConfigureAwait(false); await channel.SendConfirmAsync($"✅ {imsg.Author.Mention} removed permission **{p.GetCommand(channel.Guild)}** from position #{index + 1}.").ConfigureAwait(false);
} }
catch (ArgumentOutOfRangeException) catch (ArgumentOutOfRangeException)
{ {
await channel.SendMessageAsync("❗️`No command on that index found.`").ConfigureAwait(false); await channel.SendErrorAsync("❗️`No command on that index found.`").ConfigureAwait(false);
} }
} }
@ -208,13 +206,13 @@ namespace NadekoBot.Modules.Permissions
{ {
if (!fromFound) if (!fromFound)
{ {
await channel.SendMessageAsync($"❗️`Can't find permission at index `#{++from}`").ConfigureAwait(false); await channel.SendErrorAsync($"Can't find permission at index `#{++from}`").ConfigureAwait(false);
return; return;
} }
if (!toFound) if (!toFound)
{ {
await channel.SendMessageAsync($"❗️`Can't find permission at index `#{++to}`").ConfigureAwait(false); await channel.SendErrorAsync($"Can't find permission at index `#{++to}`").ConfigureAwait(false);
return; return;
} }
} }
@ -264,14 +262,14 @@ namespace NadekoBot.Modules.Permissions
}, (id, old) => { old.RootPermission = config.RootPermission; return old; }); }, (id, old) => { old.RootPermission = config.RootPermission; return old; });
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
} }
await channel.SendMessageAsync($"✅ `Moved permission:` \"{fromPerm.GetCommand(channel.Guild)}\" `from #{++from} to #{++to}.`").ConfigureAwait(false); await channel.SendConfirmAsync($"`Moved permission:` \"{fromPerm.GetCommand(channel.Guild)}\" `from #{++from} to #{++to}.`").ConfigureAwait(false);
return; return;
} }
catch (Exception e) when (e is ArgumentOutOfRangeException || e is IndexOutOfRangeException) catch (Exception e) when (e is ArgumentOutOfRangeException || e is IndexOutOfRangeException)
{ {
} }
} }
await channel.SendMessageAsync("`Invalid index(es) specified.`").ConfigureAwait(false); await channel.SendErrorAsync("`Invalid index(es) specified.`").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -300,7 +298,7 @@ namespace NadekoBot.Modules.Permissions
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
} }
await channel.SendMessageAsync($"{(action.Value ? " Allowed" : "🆗 Denied")} usage of `{command.Text}` command on this server.").ConfigureAwait(false); await channel.SendConfirmAsync($"{(action.Value ? " Allowed" : "🆗 Denied")} usage of `{command.Text}` command on this server.").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -328,7 +326,7 @@ namespace NadekoBot.Modules.Permissions
}, (id, old) => { old.RootPermission = config.RootPermission; return old; }); }, (id, old) => { old.RootPermission = config.RootPermission; return old; });
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
} }
await channel.SendMessageAsync($"{(action.Value ? " Allowed" : "🆗 Denied")} usage of **`{module.Name}`** module on this server.").ConfigureAwait(false); await channel.SendConfirmAsync($"{(action.Value ? " Allowed" : "🆗 Denied")} usage of **`{module.Name}`** module on this server.").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -356,7 +354,7 @@ namespace NadekoBot.Modules.Permissions
}, (id, old) => { old.RootPermission = config.RootPermission; return old; }); }, (id, old) => { old.RootPermission = config.RootPermission; return old; });
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
} }
await channel.SendMessageAsync($"{(action.Value ? " Allowed" : "🆗 Denied")} usage of `{command.Text}` command for `{user}` user.").ConfigureAwait(false); await channel.SendConfirmAsync($"{(action.Value ? " Allowed" : "🆗 Denied")} usage of `{command.Text}` command for `{user}` user.").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -384,7 +382,7 @@ namespace NadekoBot.Modules.Permissions
}, (id, old) => { old.RootPermission = config.RootPermission; return old; }); }, (id, old) => { old.RootPermission = config.RootPermission; return old; });
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
} }
await channel.SendMessageAsync($"{(action.Value ? " Allowed" : "🆗 Denied")} usage of `{module.Name}` module for `{user}` user.").ConfigureAwait(false); await channel.SendConfirmAsync($"{(action.Value ? " Allowed" : "🆗 Denied")} usage of `{module.Name}` module for `{user}` user.").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -412,7 +410,7 @@ namespace NadekoBot.Modules.Permissions
}, (id, old) => { old.RootPermission = config.RootPermission; return old; }); }, (id, old) => { old.RootPermission = config.RootPermission; return old; });
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
} }
await channel.SendMessageAsync($"{(action.Value ? " Allowed" : "🆗 Denied")} usage of `{command.Text}` command for `{role}` role.").ConfigureAwait(false); await channel.SendConfirmAsync($"{(action.Value ? " Allowed" : "🆗 Denied")} usage of `{command.Text}` command for `{role}` role.").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -440,7 +438,7 @@ namespace NadekoBot.Modules.Permissions
}, (id, old) => { old.RootPermission = config.RootPermission; return old; }); }, (id, old) => { old.RootPermission = config.RootPermission; return old; });
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
} }
await channel.SendMessageAsync($"{(action.Value ? " Allowed" : "🆗 Denied")} usage of `{module.Name}` module for `{role}` role.").ConfigureAwait(false); await channel.SendConfirmAsync($"{(action.Value ? " Allowed" : "🆗 Denied")} usage of `{module.Name}` module for `{role}` role.").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -471,9 +469,9 @@ namespace NadekoBot.Modules.Permissions
} }
} }
catch (Exception ex) { catch (Exception ex) {
Console.WriteLine(ex); _log.Error(ex);
} }
await channel.SendMessageAsync($"{(action.Value ? " Allowed" : "🆗 Denied")} usage of `{command.Text}` command for `{chnl}` channel.").ConfigureAwait(false); await channel.SendConfirmAsync($"{(action.Value ? " Allowed" : "🆗 Denied")} usage of `{command.Text}` command for `{chnl}` channel.").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -501,7 +499,7 @@ namespace NadekoBot.Modules.Permissions
}, (id, old) => { old.RootPermission = config.RootPermission; return old; }); }, (id, old) => { old.RootPermission = config.RootPermission; return old; });
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
} }
await channel.SendMessageAsync($"{(action.Value ? " Allowed" : "🆗 Denied")} usage of `{module.Name}` module for `{chnl}` channel.").ConfigureAwait(false); await channel.SendConfirmAsync($"{(action.Value ? " Allowed" : "🆗 Denied")} usage of `{module.Name}` module for `{chnl}` channel.").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -529,7 +527,7 @@ namespace NadekoBot.Modules.Permissions
}, (id, old) => { old.RootPermission = config.RootPermission; return old; }); }, (id, old) => { old.RootPermission = config.RootPermission; return old; });
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
} }
await channel.SendMessageAsync($"{(action.Value ? " Allowed" : "🆗 Denied")} usage of `ALL MODULES` for `{chnl}` channel.").ConfigureAwait(false); await channel.SendConfirmAsync($"{(action.Value ? " Allowed" : "🆗 Denied")} usage of `ALL MODULES` for `{chnl}` channel.").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -557,7 +555,7 @@ namespace NadekoBot.Modules.Permissions
}, (id, old) => { old.RootPermission = config.RootPermission; return old; }); }, (id, old) => { old.RootPermission = config.RootPermission; return old; });
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
} }
await channel.SendMessageAsync($"{(action.Value ? " Allowed" : "🆗 Denied")} usage of `ALL MODULES` for `{role}` role.").ConfigureAwait(false); await channel.SendConfirmAsync($"{(action.Value ? " Allowed" : "🆗 Denied")} usage of `ALL MODULES` for `{role}` role.").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -585,7 +583,7 @@ namespace NadekoBot.Modules.Permissions
}, (id, old) => { old.RootPermission = config.RootPermission; return old; }); }, (id, old) => { old.RootPermission = config.RootPermission; return old; });
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
} }
await channel.SendMessageAsync($"{(action.Value ? " Allowed" : "🆗 Denied")} usage of `ALL MODULES` for `{user}` user.").ConfigureAwait(false); await channel.SendConfirmAsync($"{(action.Value ? " Allowed" : "🆗 Denied")} usage of `ALL MODULES` for `{user}` user.").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -624,7 +622,7 @@ namespace NadekoBot.Modules.Permissions
}, (id, old) => { old.RootPermission = config.RootPermission; return old; }); }, (id, old) => { old.RootPermission = config.RootPermission; return old; });
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
} }
await channel.SendMessageAsync($"{(action.Value ? " Allowed" : "🆗 Denied")} usage of `ALL MODULES` on this server.").ConfigureAwait(false); await channel.SendConfirmAsync($"{(action.Value ? " Allowed" : "🆗 Denied")} usage of `ALL MODULES` on this server.").ConfigureAwait(false);
} }
} }
} }

View File

@ -0,0 +1,15 @@
using System.Collections.Generic;
namespace NadekoBot.Modules.Pokemon
{
class PokeStats
{
//Health left
public int Hp { get; set; } = 500;
public int MaxHp { get; } = 500;
//Amount of moves made since last time attacked
public int MovesMade { get; set; } = 0;
//Last people attacked
public List<ulong> LastAttacked { get; set; } = new List<ulong>();
}
}

View File

@ -0,0 +1,375 @@
using Discord.Commands;
using NadekoBot.Attributes;
using NadekoBot.Extensions;
using System.Linq;
using NadekoBot.Services;
using NadekoBot.Services.Database.Models;
using System.Collections.Generic;
using System.Threading.Tasks;
using Discord;
using NLog;
using System;
using Newtonsoft.Json;
using System.IO;
using System.Collections.Concurrent;
using static NadekoBot.Modules.Gambling.Gambling;
namespace NadekoBot.Modules.Pokemon
{
[NadekoModule("Pokemon", ">")]
public partial class Pokemon : DiscordModule
{
private static List<PokemonType> PokemonTypes = new List<PokemonType>();
private static ConcurrentDictionary<ulong, PokeStats> Stats = new ConcurrentDictionary<ulong, PokeStats>();
public const string PokemonTypesFile = "data/pokemon_types.json";
private Logger _pokelog { get; }
public Pokemon() : base()
{
_pokelog = LogManager.GetCurrentClassLogger();
if (File.Exists(PokemonTypesFile))
{
PokemonTypes = JsonConvert.DeserializeObject<List<PokemonType>>(File.ReadAllText(PokemonTypesFile));
}
else
{
_pokelog.Warn(PokemonTypesFile + " is missing. Pokemon types not loaded.");
}
}
private int GetDamage(PokemonType usertype, PokemonType targetType)
{
var rng = new Random();
int damage = rng.Next(40, 60);
foreach (PokemonMultiplier Multiplier in usertype.Multipliers)
{
if (Multiplier.Type == targetType.Name)
{
var multiplier = Multiplier.Multiplication;
damage = (int)(damage * multiplier);
}
}
return damage;
}
private PokemonType GetPokeType(ulong id)
{
Dictionary<ulong, string> setTypes;
using (var uow = DbHandler.UnitOfWork())
{
setTypes = uow.PokeGame.GetAll().ToDictionary(x => x.UserId, y => y.type);
}
if (setTypes.ContainsKey(id))
{
return StringToPokemonType(setTypes[id]);
}
int count = PokemonTypes.Count;
int remainder = Math.Abs((int)(id % (ulong)count));
return PokemonTypes[remainder];
}
private PokemonType StringToPokemonType(string v)
{
var str = v?.ToUpperInvariant();
var list = PokemonTypes;
foreach (PokemonType p in list)
{
if (str == p.Name)
{
return p;
}
}
return null;
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Attack(IUserMessage umsg, string move, IGuildUser targetUser = null)
{
var channel = (ITextChannel)umsg.Channel;
IGuildUser user = (IGuildUser)umsg.Author;
if (string.IsNullOrWhiteSpace(move)) {
return;
}
if (targetUser == null)
{
await channel.SendMessageAsync("No such person.").ConfigureAwait(false);
return;
}
else if (targetUser == user)
{
await channel.SendMessageAsync("You can't attack yourself.").ConfigureAwait(false);
return;
}
// Checking stats first, then move
//Set up the userstats
PokeStats userStats;
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 channel.SendMessageAsync($"{user.Mention} has fainted and was not able to move!").ConfigureAwait(false);
return;
}
if (userStats.MovesMade >= 5)
{
await channel.SendMessageAsync($"{user.Mention} has used too many moves in a row and was not able to move!").ConfigureAwait(false);
return;
}
if (userStats.LastAttacked.Contains(targetUser.Id))
{
await channel.SendMessageAsync($"{user.Mention} can't attack again without retaliation!").ConfigureAwait(false);
return;
}
//get target stats
PokeStats targetStats;
targetStats = Stats.GetOrAdd(targetUser.Id, new PokeStats());
//If target's HP is below 0, no use attacking
if (targetStats.Hp <= 0)
{
await channel.SendMessageAsync($"{targetUser.Mention} has already fainted!").ConfigureAwait(false);
return;
}
//Check whether move can be used
PokemonType userType = GetPokeType(user.Id);
var enabledMoves = userType.Moves;
if (!enabledMoves.Contains(move.ToLowerInvariant()))
{
await channel.SendMessageAsync($"{user.Mention} is not able to use **{move}**. Type {NadekoBot.ModulePrefixes[typeof(Pokemon).Name]}ml to see moves").ConfigureAwait(false);
return;
}
//get target type
PokemonType targetType = GetPokeType(targetUser.Id);
//generate damage
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";
//Damage type
if (damage < 40)
{
response += "\nIt's not effective..";
}
else if (damage > 60)
{
response += "\nIt's super effective!";
}
else
{
response += "\nIt's somewhat effective";
}
//check fainted
if (targetStats.Hp <= 0)
{
response += $"\n**{targetUser.Mention}** has fainted!";
}
else
{
response += $"\n**{targetUser.Mention}** has {targetStats.Hp} HP remaining";
}
//update other stats
userStats.LastAttacked.Add(targetUser.Id);
userStats.MovesMade++;
targetStats.MovesMade = 0;
if (targetStats.LastAttacked.Contains(user.Id))
{
targetStats.LastAttacked.Remove(user.Id);
}
//update dictionary
//This can stay the same right?
Stats[user.Id] = userStats;
Stats[targetUser.Id] = targetStats;
await channel.SendMessageAsync(response).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Movelist(IUserMessage umsg)
{
var channel = (ITextChannel)umsg.Channel;
IGuildUser user = (IGuildUser)umsg.Author;
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 channel.SendMessageAsync(str).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Heal(IUserMessage umsg, IGuildUser targetUser = null)
{
var channel = (ITextChannel)umsg.Channel;
IGuildUser user = (IGuildUser)umsg.Author;
if (targetUser == null) {
await channel.SendMessageAsync("No such person.").ConfigureAwait(false);
return;
}
if (Stats.ContainsKey(targetUser.Id))
{
var targetStats = Stats[targetUser.Id];
if (targetStats.Hp == targetStats.MaxHp)
{
await channel.SendMessageAsync($"{targetUser.Mention} already has full HP!").ConfigureAwait(false);
return;
}
//Payment~
var amount = 1;
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 channel.SendMessageAsync($"{user.Mention} You don't have enough {CurrencyName}s.").ConfigureAwait(false); } catch { }
return;
}
}
//healing
targetStats.Hp = targetStats.MaxHp;
if (targetStats.Hp < 0)
{
//Could heal only for half HP?
Stats[targetUser.Id].Hp = (targetStats.MaxHp / 2);
if (target == "yourself")
{
await channel.SendMessageAsync($"You revived yourself with one {CurrencySign}").ConfigureAwait(false);
}
else
{
await channel.SendMessageAsync($"{user.Mention} revived {targetUser.Mention} with one {CurrencySign}").ConfigureAwait(false);
}
return;
}
await channel.SendMessageAsync($"{user.Mention} healed {targetUser.Mention} with one {CurrencySign}").ConfigureAwait(false);
return;
}
else
{
await channel.SendMessageAsync($"{targetUser.Mention} already has full HP!").ConfigureAwait(false);
}
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Type(IUserMessage umsg, IGuildUser targetUser = null)
{
var channel = (ITextChannel)umsg.Channel;
IGuildUser user = (IGuildUser)umsg.Author;
if (targetUser == null)
{
return;
}
var pType = GetPokeType(targetUser.Id);
await channel.SendMessageAsync($"Type of {targetUser.Mention} is **{pType.Name.ToLowerInvariant()}**{pType.Icon}").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Settype(IUserMessage umsg, [Remainder] string typeTargeted = null)
{
var channel = (ITextChannel)umsg.Channel;
IGuildUser user = (IGuildUser)umsg.Author;
var targetType = StringToPokemonType(typeTargeted);
if (targetType == null)
{
await 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))).WithOkColor().Build()).ConfigureAwait(false);
return;
}
if (targetType == GetPokeType(user.Id))
{
await channel.SendMessageAsync($"Your type is already {targetType.Name.ToLowerInvariant()}{targetType.Icon}").ConfigureAwait(false);
return;
}
//Payment~
var amount = 1;
if (amount > 0)
{
if (!await CurrencyHandler.RemoveCurrencyAsync(user, $"{user.Mention} change type to {typeTargeted}", amount, true).ConfigureAwait(false))
{
try { await channel.SendMessageAsync($"{user.Mention} You don't have enough {CurrencyName}s.").ConfigureAwait(false); } catch { }
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 pt = new UserPokeTypes
{
UserId = user.Id,
type = targetType.Name,
};
if (!setTypes.ContainsKey(user.Id))
{
//create user in db
uow.PokeGame.Add(pt);
}
else
{
//update user in db
var pokeUserCmd = pokeUsers.Where(p => p.UserId == user.Id).FirstOrDefault();
pokeUserCmd.type = targetType.Name;
uow.PokeGame.Update(pokeUserCmd);
}
await uow.CompleteAsync();
}
//Now for the response
await channel.SendMessageAsync($"Set type of {user.Mention} to {typeTargeted}{targetType.Icon} for a {CurrencySign}").ConfigureAwait(false);
}
}
}

View File

@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NadekoBot.Modules.Pokemon
{
public class PokemonType
{
public PokemonType(string n, string i, string[] m, List<PokemonMultiplier> multi)
{
Name = n;
Icon = i;
Moves = m;
Multipliers = multi;
}
public string Name { get; set; }
public List<PokemonMultiplier> Multipliers { get; set; }
public string Icon { get; set; }
public string[] Moves { get; set; }
}
public class PokemonMultiplier
{
public PokemonMultiplier(string t, double m)
{
Type = t;
Multiplication = m;
}
public string Type { get; set; }
public double Multiplication { get; set; }
}
}

View File

@ -10,6 +10,7 @@ using NLog;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net.Http; using System.Net.Http;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace NadekoBot.Modules.Searches namespace NadekoBot.Modules.Searches
@ -19,14 +20,37 @@ namespace NadekoBot.Modules.Searches
[Group] [Group]
public class AnimeSearchCommands public class AnimeSearchCommands
{ {
private Logger _log; private static Timer anilistTokenRefresher { get; }
private static Logger _log { get; }
private static string anilistToken { get; set; }
private string anilistToken { get; set; } static AnimeSearchCommands()
private DateTime lastRefresh { get; set; }
public AnimeSearchCommands()
{ {
_log = LogManager.GetCurrentClassLogger(); _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"},
};
using (var http = new HttpClient())
{
http.AddFakeHeaders();
var formContent = new FormUrlEncodedContent(headers);
var response = await http.PostAsync("http://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)
{
_log.Error(ex);
}
}, null, TimeSpan.FromSeconds(0), TimeSpan.FromMinutes(29));
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -40,34 +64,22 @@ namespace NadekoBot.Modules.Searches
var animeData = await GetAnimeData(query).ConfigureAwait(false); var animeData = await GetAnimeData(query).ConfigureAwait(false);
var embed = new Discord.API.Embed() if (animeData == null)
{ {
Description = animeData.Synopsis, await umsg.Channel.SendErrorAsync("Failed finding that animu.").ConfigureAwait(false);
Title = animeData.title_english, return;
Url = animeData.Link,
Image = new Discord.API.EmbedImage() {
Url = animeData.image_url_lge
},
Fields = new[] {
new Discord.API.EmbedField() {
Inline = true,
Name = "Episodes",
Value = animeData.total_episodes.ToString()
},
new Discord.API.EmbedField() {
Inline = true,
Name = "Status",
Value = animeData.AiringStatus.ToString()
},
new Discord.API.EmbedField() {
Inline = true,
Name = "Genres",
Value = String.Join(", ", animeData.Genres)
} }
},
Color = NadekoBot.OkColor var embed = new EmbedBuilder().WithColor(NadekoBot.OkColor)
}; .WithDescription(animeData.Synopsis.Replace("<br>", Environment.NewLine))
await channel.EmbedAsync(embed).ConfigureAwait(false); .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"));
await channel.EmbedAsync(embed.Build()).ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -79,38 +91,25 @@ namespace NadekoBot.Modules.Searches
if (string.IsNullOrWhiteSpace(query)) if (string.IsNullOrWhiteSpace(query))
return; return;
var animeData = await GetMangaData(query).ConfigureAwait(false); var mangaData = await GetMangaData(query).ConfigureAwait(false);
var embed = new Discord.API.Embed() if (mangaData == null)
{ {
Description = animeData.Synopsis, await umsg.Channel.SendErrorAsync("Failed finding that mango.").ConfigureAwait(false);
Title = animeData.title_english, return;
Url = animeData.Link,
Image = new Discord.API.EmbedImage()
{
Url = animeData.image_url_lge
},
Fields = new[] {
new Discord.API.EmbedField() {
Inline = true,
Name = "Chapters",
Value = animeData.total_chapters.ToString()
},
new Discord.API.EmbedField() {
Inline = true,
Name = "Status",
Value = animeData.publishing_status.ToString()
},
new Discord.API.EmbedField() {
Inline = true,
Name = "Genres",
Value = String.Join(", ", animeData.Genres)
} }
},
Color = NadekoBot.OkColor
};
await channel.EmbedAsync(embed).ConfigureAwait(false); var embed = new EmbedBuilder().WithColor(NadekoBot.OkColor)
.WithDescription(mangaData.Synopsis.Replace("<br>", Environment.NewLine))
.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"));
await channel.EmbedAsync(embed.Build()).ConfigureAwait(false);
} }
private async Task<AnimeResult> GetAnimeData(string query) private async Task<AnimeResult> GetAnimeData(string query)
@ -119,7 +118,6 @@ namespace NadekoBot.Modules.Searches
throw new ArgumentNullException(nameof(query)); throw new ArgumentNullException(nameof(query));
try try
{ {
await RefreshAnilistToken().ConfigureAwait(false);
var link = "http://anilist.co/api/anime/search/" + Uri.EscapeUriString(query); var link = "http://anilist.co/api/anime/search/" + Uri.EscapeUriString(query);
using (var http = new HttpClient()) using (var http = new HttpClient())
@ -131,43 +129,19 @@ namespace NadekoBot.Modules.Searches
return await Task.Run(() => { try { return JsonConvert.DeserializeObject<AnimeResult>(aniData); } catch { return null; } }).ConfigureAwait(false); return await Task.Run(() => { try { return JsonConvert.DeserializeObject<AnimeResult>(aniData); } catch { return null; } }).ConfigureAwait(false);
} }
} }
catch (Exception ex) { catch (Exception ex)
{
_log.Warn(ex, "Failed anime search for {0}", query); _log.Warn(ex, "Failed anime search for {0}", query);
return null; return null;
} }
} }
private async Task RefreshAnilistToken()
{
if (DateTime.Now - lastRefresh > TimeSpan.FromMinutes(29))
lastRefresh = DateTime.Now;
else
{
return;
}
var headers = new Dictionary<string, string> {
{"grant_type", "client_credentials"},
{"client_id", "kwoth-w0ki9"},
{"client_secret", "Qd6j4FIAi1ZK6Pc7N7V4Z"},
};
using (var http = new HttpClient())
{
http.AddFakeHeaders();
var formContent = new FormUrlEncodedContent(headers);
var response = await http.PostAsync("http://anilist.co/api/auth/access_token", formContent).ConfigureAwait(false);
var stringContent = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
anilistToken = JObject.Parse(stringContent)["access_token"].ToString();
}
}
private async Task<MangaResult> GetMangaData(string query) private async Task<MangaResult> GetMangaData(string query)
{ {
if (string.IsNullOrWhiteSpace(query)) if (string.IsNullOrWhiteSpace(query))
throw new ArgumentNullException(nameof(query)); throw new ArgumentNullException(nameof(query));
try try
{ {
await RefreshAnilistToken().ConfigureAwait(false);
using (var http = new HttpClient()) using (var http = new HttpClient())
{ {
var res = await http.GetStringAsync("http://anilist.co/api/manga/search/" + Uri.EscapeUriString(query) + $"?access_token={anilistToken}").ConfigureAwait(false); var res = await http.GetStringAsync("http://anilist.co/api/manga/search/" + Uri.EscapeUriString(query) + $"?access_token={anilistToken}").ConfigureAwait(false);

View File

@ -1,6 +1,7 @@
using Discord; using Discord;
using Discord.Commands; using Discord.Commands;
using NadekoBot.Attributes; using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Modules.Searches.Models; using NadekoBot.Modules.Searches.Models;
using NadekoBot.Services; using NadekoBot.Services;
using Newtonsoft.Json; using Newtonsoft.Json;
@ -19,11 +20,11 @@ namespace NadekoBot.Modules.Searches
[Group] [Group]
public class JokeCommands public class JokeCommands
{ {
private List<WoWJoke> wowJokes = new List<WoWJoke>(); private static List<WoWJoke> wowJokes { get; } = new List<WoWJoke>();
private List<MagicItem> magicItems; private static List<MagicItem> magicItems { get; } = new List<MagicItem>();
private Logger _log; private static Logger _log { get; }
public JokeCommands() static JokeCommands()
{ {
_log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
if (File.Exists("data/wowjokes.json")) if (File.Exists("data/wowjokes.json"))
@ -43,61 +44,63 @@ namespace NadekoBot.Modules.Searches
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task Yomama(IUserMessage umsg) public async Task Yomama(IUserMessage msg)
{ {
var channel = (ITextChannel)umsg.Channel;
using (var http = new HttpClient()) using (var http = new HttpClient())
{ {
var response = await http.GetStringAsync("http://api.yomomma.info/").ConfigureAwait(false); var response = await http.GetStringAsync("http://api.yomomma.info/").ConfigureAwait(false);
await channel.SendMessageAsync("`" + JObject.Parse(response)["joke"].ToString() + "` 😆").ConfigureAwait(false); System.Console.WriteLine(response);
await msg.Channel.SendConfirmAsync(JObject.Parse(response)["joke"].ToString() + " 😆").ConfigureAwait(false);
} }
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task Randjoke(IUserMessage umsg) public async Task Randjoke(IUserMessage msg)
{ {
var channel = (ITextChannel)umsg.Channel;
using (var http = new HttpClient()) using (var http = new HttpClient())
{ {
var response = await http.GetStringAsync("http://tambal.azurewebsites.net/joke/random").ConfigureAwait(false); var response = await http.GetStringAsync("http://tambal.azurewebsites.net/joke/random").ConfigureAwait(false);
await channel.SendMessageAsync("`" + JObject.Parse(response)["joke"].ToString() + "` 😆").ConfigureAwait(false); await msg.Channel.SendConfirmAsync(JObject.Parse(response)["joke"].ToString() + " 😆").ConfigureAwait(false);
} }
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task ChuckNorris(IUserMessage umsg) public async Task ChuckNorris(IUserMessage msg)
{ {
var channel = (ITextChannel)umsg.Channel;
using (var http = new HttpClient()) using (var http = new HttpClient())
{ {
var response = await http.GetStringAsync("http://api.icndb.com/jokes/random/").ConfigureAwait(false); var response = await http.GetStringAsync("http://api.icndb.com/jokes/random/").ConfigureAwait(false);
await channel.SendMessageAsync("`" + JObject.Parse(response)["value"]["joke"].ToString() + "` 😆").ConfigureAwait(false); await msg.Channel.SendConfirmAsync(JObject.Parse(response)["value"]["joke"].ToString() + " 😆").ConfigureAwait(false);
} }
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task WowJoke(IUserMessage umsg) public async Task WowJoke(IUserMessage msg)
{ {
var channel = (ITextChannel)umsg.Channel;
if (!wowJokes.Any()) if (!wowJokes.Any())
{ {
await msg.Channel.SendErrorAsync("Jokes not loaded.").ConfigureAwait(false);
return;
} }
await channel.SendMessageAsync(wowJokes[new NadekoRandom().Next(0, wowJokes.Count)].ToString()); var joke = wowJokes[new NadekoRandom().Next(0, wowJokes.Count)];
await msg.Channel.SendConfirmAsync(joke.Question, joke.Answer).ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task MagicItem(IUserMessage umsg) public async Task MagicItem(IUserMessage msg)
{ {
var channel = (ITextChannel)umsg.Channel; if (!wowJokes.Any())
var rng = new NadekoRandom(); {
var item = magicItems[rng.Next(0, magicItems.Count)].ToString(); await msg.Channel.SendErrorAsync("MagicItems not loaded.").ConfigureAwait(false);
return;
}
var item = magicItems[new NadekoRandom().Next(0, magicItems.Count)];
await channel.SendMessageAsync(item).ConfigureAwait(false); await msg.Channel.SendConfirmAsync("✨" + item.Name, item.Description).ConfigureAwait(false);
} }
} }
} }

View File

@ -1,6 +1,7 @@
using Discord; using Discord;
using Discord.Commands; using Discord.Commands;
using NadekoBot.Attributes; using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Services; using NadekoBot.Services;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using System; using System;
@ -23,7 +24,7 @@ namespace NadekoBot.Modules.Searches
obj["name"].GetHashCode(); obj["name"].GetHashCode();
} }
private string[] trashTalk { get; } = { "Better ban your counters. You are going to carry the game anyway.", private static string[] trashTalk { get; } = { "Better ban your counters. You are going to carry the game anyway.",
"Go with the flow. Don't think. Just ban one of these.", "Go with the flow. Don't think. Just ban one of these.",
"DONT READ BELOW! Ban Urgot mid OP 100%. Im smurf Diamond 1.", "DONT READ BELOW! Ban Urgot mid OP 100%. Im smurf Diamond 1.",
"Ask your teammates what would they like to play, and ban that.", "Ask your teammates what would they like to play, and ban that.",
@ -50,23 +51,19 @@ namespace NadekoBot.Modules.Searches
$"limit={showCount}") $"limit={showCount}")
.ConfigureAwait(false))["data"] as JArray; .ConfigureAwait(false))["data"] as JArray;
var dataList = data.Distinct(new ChampionNameComparer()).Take(showCount).ToList(); var dataList = data.Distinct(new ChampionNameComparer()).Take(showCount).ToList();
var sb = new StringBuilder(); var eb = new EmbedBuilder().WithOkColor().WithTitle(Format.Underline($"{dataList.Count} most banned champions"));
sb.AppendLine($"**Showing {dataList.Count} top banned champions.**");
sb.AppendLine($"`{trashTalk[new NadekoRandom().Next(0, trashTalk.Length)]}`");
for (var i = 0; i < dataList.Count; i++) for (var i = 0; i < dataList.Count; i++)
{ {
if (i % 2 == 0 && i != 0) var champ = dataList[i];
sb.AppendLine(); eb.AddField(efb => efb.WithName(champ["name"].ToString()).WithValue(champ["general"]["banRate"] + "%").WithIsInline(true));
sb.Append($"`{i + 1}.` **{dataList[i]["name"]}** {dataList[i]["general"]["banRate"]}% ");
//sb.AppendLine($" ({dataList[i]["general"]["banRate"]}%)");
} }
await channel.SendMessageAsync(sb.ToString()).ConfigureAwait(false); await channel.EmbedAsync(eb.Build(), Format.Italics(trashTalk[new NadekoRandom().Next(0, trashTalk.Length)])).ConfigureAwait(false);
} }
} }
catch (Exception) catch (Exception)
{ {
await channel.SendMessageAsync($":anger: `Something went wrong.`").ConfigureAwait(false); await channel.SendMessageAsync("Something went wrong.").ConfigureAwait(false);
} }
} }
} }

View File

@ -25,20 +25,11 @@ namespace NadekoBot.Modules.Searches
using (var http = new HttpClient(handler)) using (var http = new HttpClient(handler))
{ {
http.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json")); var rawJson = await http.GetStringAsync("https://memegen.link/api/templates/").ConfigureAwait(false);
string rawJson = "";
try
{
rawJson = await http.GetStringAsync("https://memegen.link/api/templates/").ConfigureAwait(false);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
var data = JsonConvert.DeserializeObject<Dictionary<string, string>>(rawJson) var data = JsonConvert.DeserializeObject<Dictionary<string, string>>(rawJson)
.Select(kvp => Path.GetFileName(kvp.Value)); .Select(kvp => Path.GetFileName(kvp.Value));
await channel.SendTableAsync(data, x => $"{x,-17}", 3); await channel.SendTableAsync(data, x => $"{x,-17}", 3).ConfigureAwait(false);
} }
} }
@ -48,9 +39,10 @@ namespace NadekoBot.Modules.Searches
{ {
var channel = (ITextChannel)umsg.Channel; var channel = (ITextChannel)umsg.Channel;
var top = Uri.EscapeDataString(topText.Replace(' ', '-')); var top = topText.Replace(' ', '-');
var bot = Uri.EscapeDataString(botText.Replace(' ', '-')); var bot = botText.Replace(' ', '-');
await channel.SendMessageAsync($"http://memegen.link/{meme}/{top}/{bot}.jpg"); await channel.SendMessageAsync($"http://memegen.link/{meme}/{top}/{bot}.jpg")
.ConfigureAwait(false);
} }
} }
} }

View File

@ -13,6 +13,8 @@ namespace NadekoBot.Modules.Searches.Models
public string description; public string description;
public string image_url_lge; public string image_url_lge;
public string[] Genres; public string[] Genres;
public string average_score;
public string Link => "http://anilist.co/anime/" + id; public string Link => "http://anilist.co/anime/" + id;
public string Synopsis => description?.Substring(0, description.Length > 500 ? 500 : description.Length) + "..."; public string Synopsis => description?.Substring(0, description.Length > 500 ? 500 : description.Length) + "...";
} }

View File

@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NadekoBot.Modules.Searches.Commands.Models
{
public class Audio
{
public string url { get; set; }
}
public class Example
{
public List<Audio> audio { get; set; }
public string text { get; set; }
}
public class GramaticalInfo
{
public string type { get; set; }
}
public class Sens
{
public object Definition { get; set; }
public List<Example> Examples { get; set; }
public GramaticalInfo Gramatical_info { get; set; }
}
public class Result
{
public string Part_of_speech { get; set; }
public List<Sens> Senses { get; set; }
public string Url { get; set; }
}
public class DefineModel
{
public List<Result> Results { get; set; }
}
}

View File

@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NadekoBot.Modules.Searches.Commands.Models
{
public struct GoogleSearchResult
{
public string Title { get; }
public string Link { get; }
public string Text { get; }
public GoogleSearchResult(string title, string link, string text)
{
this.Title = title;
this.Link = link;
this.Text = text;
}
}
}

View File

@ -4,7 +4,5 @@
{ {
public string Name { get; set; } public string Name { get; set; }
public string Description { get; set; } public string Description { get; set; }
public override string ToString() =>
$"✨`{Name}`\n\t*{Description}*";
} }
} }

View File

@ -10,6 +10,7 @@ namespace NadekoBot.Modules.Searches.Models
public int total_volumes; public int total_volumes;
public string description; public string description;
public string[] Genres; public string[] Genres;
public string average_score;
public string Link => "http://anilist.co/manga/" + id; public string Link => "http://anilist.co/manga/" + id;
public string Synopsis => description?.Substring(0, description.Length > 500 ? 500 : description.Length) + "..."; public string Synopsis => description?.Substring(0, description.Length > 500 ? 500 : description.Length) + "...";
} }

View File

@ -0,0 +1,67 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace NadekoBot.Modules.Searches.Models
{
public class OverwatchApiModel
{
public OverwatchPlayer Player { get; set; }
public class OverwatchPlayer
{
public Data data { get; set; }
public class Data
{
public bool Missing { get; set; } = false;
public string username { get; set; }
public int level { get; set; }
public string avatar { get; set; }
public string levelFrame { get; set; }
public string star { get; set; }
[JsonProperty("games")]
public OverwatchGames Games { get; set; }
[JsonProperty("playtime")]
public OverwatchPlaytime Playtime { get; set; }
[JsonProperty("competitive")]
public OverwatchCompetitive Competitive { get; set; }
public class OverwatchGames
{
[JsonProperty("quick")]
public OverwatchQG Quick { get; set; }
[JsonProperty("competitive")]
public OverwatchCOMP Competitive { get; set; }
public class OverwatchQG
{
public string wins { get; set; }
}
public class OverwatchCOMP
{
public string wins { get; set; }
public int lost { get; set; }
public string played { get; set; }
}
}
public class OverwatchCompetitive
{
public string rank { get; set; }
public string rank_img { get; set; }
}
public class OverwatchPlaytime
{
public string quick { get; set; }
public string competitive { get; set; }
}
}
}
//This is to strip the html from patch notes content
internal static string StripHTML(string input)
{
var re = Regex.Replace(input, "<.*?>", String.Empty);
re = Regex.Replace(re, "&#160;", $@" ");
return re;
}
}
}

View File

@ -18,8 +18,7 @@ namespace NadekoBot.Modules.Searches.Models
public int SPD { get; set; } public int SPD { get; set; }
public int SPE { get; set; } public int SPE { get; set; }
public override string ToString() => $@" public override string ToString() => $@"**HP:** {HP,-4} **ATK:** {ATK,-4} **DEF:** {DEF,-4}
**HP:** {HP,-4} **ATK:** {ATK,-4} **DEF:** {DEF,-4}
**SPA:** {SPA,-4} **SPD:** {SPD,-4} **SPE:** {SPE,-4}"; **SPA:** {SPA,-4} **SPD:** {SPD,-4} **SPE:** {SPE,-4}";
} }
public int Id { get; set; } public int Id { get; set; }

View File

@ -0,0 +1,67 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NadekoBot.Modules.Searches.Commands.Models
{
public class Coord
{
public double lon { get; set; }
public double lat { get; set; }
}
public class Weather
{
public int id { get; set; }
public string main { get; set; }
public string description { get; set; }
public string icon { get; set; }
}
public class Main
{
public double temp { get; set; }
public int pressure { get; set; }
public int humidity { get; set; }
public double temp_min { get; set; }
public double temp_max { get; set; }
}
public class Wind
{
public double speed { get; set; }
public double deg { get; set; }
}
public class Clouds
{
public int all { get; set; }
}
public class Sys
{
public int type { get; set; }
public int id { get; set; }
public double message { get; set; }
public string country { get; set; }
public double sunrise { get; set; }
public double sunset { get; set; }
}
public class WeatherData
{
public Coord coord { get; set; }
public List<Weather> weather { get; set; }
public Main main { get; set; }
public int visibility { get; set; }
public Wind wind { get; set; }
public Clouds clouds { get; set; }
public int dt { get; set; }
public Sys sys { get; set; }
public int id { get; set; }
public string name { get; set; }
public int cod { get; set; }
}
}

View File

@ -1,4 +1,7 @@
using Newtonsoft.Json; using Discord;
using Discord.API;
using NadekoBot.Extensions;
using Newtonsoft.Json;
using System; using System;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -32,6 +35,17 @@ namespace NadekoBot.Modules.Searches.Commands.OMDB
public string Plot { get; set; } public string Plot { get; set; }
public string Poster { get; set; } public string Poster { get; set; }
public Embed GetEmbed() =>
new EmbedBuilder().WithOkColor()
.WithTitle(Title)
.WithUrl($"http://www.imdb.com/title/{ImdbId}/")
.WithDescription(Plot)
.AddField(efb => efb.WithName("Rating").WithValue(ImdbRating).WithIsInline(true))
.AddField(efb => efb.WithName("Genre").WithValue(Genre).WithIsInline(true))
.AddField(efb => efb.WithName("Year").WithValue(Year).WithIsInline(true))
.WithImage(eib => eib.WithUrl(Poster))
.Build();
public override string ToString() => public override string ToString() =>
$@"`Title:` {Title} $@"`Title:` {Title}
`Year:` {Year} `Year:` {Year}

View File

@ -18,9 +18,9 @@ namespace NadekoBot.Modules.Searches
[Group] [Group]
public class OsuCommands public class OsuCommands
{ {
private Logger _log; private static Logger _log { get; }
public OsuCommands() static OsuCommands()
{ {
_log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
} }
@ -52,7 +52,7 @@ namespace NadekoBot.Modules.Searches
} }
catch (Exception ex) catch (Exception ex)
{ {
await channel.SendMessageAsync("💢 Failed retrieving osu signature :\\").ConfigureAwait(false); await channel.SendErrorAsync("Failed retrieving osu signature.").ConfigureAwait(false);
_log.Warn(ex, "Osu command failed"); _log.Warn(ex, "Osu command failed");
} }
} }
@ -66,7 +66,7 @@ namespace NadekoBot.Modules.Searches
if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.OsuApiKey)) if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.OsuApiKey))
{ {
await channel.SendMessageAsync("💢 An osu! API key is required.").ConfigureAwait(false); await channel.SendErrorAsync("An osu! API key is required.").ConfigureAwait(false);
return; return;
} }
@ -90,7 +90,7 @@ namespace NadekoBot.Modules.Searches
} }
catch (Exception ex) catch (Exception ex)
{ {
await channel.SendMessageAsync("Something went wrong."); await channel.SendErrorAsync("Something went wrong.");
_log.Warn(ex, "Osub command failed"); _log.Warn(ex, "Osub command failed");
} }
} }
@ -102,13 +102,13 @@ namespace NadekoBot.Modules.Searches
var channel = (ITextChannel)umsg.Channel; var channel = (ITextChannel)umsg.Channel;
if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.OsuApiKey)) if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.OsuApiKey))
{ {
await channel.SendMessageAsync("💢 An osu! API key is required.").ConfigureAwait(false); await channel.SendErrorAsync("An osu! API key is required.").ConfigureAwait(false);
return; return;
} }
if (string.IsNullOrWhiteSpace(user)) if (string.IsNullOrWhiteSpace(user))
{ {
await channel.SendMessageAsync("💢 Please provide a username.").ConfigureAwait(false); await channel.SendErrorAsync("Please provide a username.").ConfigureAwait(false);
return; return;
} }
using (var http = new HttpClient()) using (var http = new HttpClient())
@ -141,7 +141,7 @@ namespace NadekoBot.Modules.Searches
} }
catch (Exception ex) catch (Exception ex)
{ {
await channel.SendMessageAsync("Something went wrong."); await channel.SendErrorAsync("Something went wrong.");
_log.Warn(ex, "Osu5 command failed"); _log.Warn(ex, "Osu5 command failed");
} }

View File

@ -0,0 +1,118 @@
using Discord;
using Discord.Commands;
using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Modules.Searches.Models;
using Newtonsoft.Json;
using NLog;
using System;
using System.Linq;
using System.Net.Http;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace NadekoBot.Modules.Searches
{
public partial class Searches
{
[Group]
public class OverwatchCommands
{
private Logger _log;
public OverwatchCommands()
{
_log = LogManager.GetCurrentClassLogger();
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Overwatch(IUserMessage umsg, string region, [Remainder] string query = null)
{
var channel = (ITextChannel)umsg.Channel;
if (string.IsNullOrWhiteSpace(query))
return;
var battletag = Regex.Replace(query, "#", "-", RegexOptions.IgnoreCase);
await channel.TriggerTypingAsync().ConfigureAwait(false);
try
{
var model = await GetProfile(region, battletag);
var rankimg = $"{model.Competitive.rank_img}";
var rank = $"{model.Competitive.rank}";
var competitiveplay = $"{model.Games.Competitive.played}";
if (string.IsNullOrWhiteSpace(rank))
{
var embed = new EmbedBuilder()
.WithAuthor(eau => eau.WithName($"{model.username}")
.WithUrl($"https://www.overbuff.com/players/pc/{battletag}")
.WithIconUrl($"{model.avatar}"))
.WithThumbnail(th => th.WithUrl("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("**Current Competitive Wins**").WithValue($"{model.Games.Competitive.wins}").WithIsInline(true))
.AddField(fb => fb.WithName("**Current Competitive Loses**").WithValue($"{model.Games.Competitive.lost}").WithIsInline(true))
.AddField(fb => fb.WithName("**Current Competitive Played**").WithValue($"{model.Games.Competitive.played}").WithIsInline(true))
.AddField(fb => fb.WithName("**Competitive Rank**").WithValue("0").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))
.WithOkColor();
await channel.EmbedAsync(embed.Build()).ConfigureAwait(false);
}
else
{
var embed = new EmbedBuilder()
.WithAuthor(eau => eau.WithName($"{model.username}")
.WithUrl($"https://www.overbuff.com/players/pc/{battletag}")
.WithIconUrl($"{model.avatar}"))
.WithThumbnail(th => th.WithUrl(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("**Current Competitive Wins**").WithValue($"{model.Games.Competitive.wins}").WithIsInline(true))
.AddField(fb => fb.WithName("**Current Competitive Loses**").WithValue($"{model.Games.Competitive.lost}").WithIsInline(true))
.AddField(fb => fb.WithName("**Current 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))
.WithOkColor();
await channel.EmbedAsync(embed.Build()).ConfigureAwait(false);
}
if (string.IsNullOrWhiteSpace(competitiveplay))
{
var embed = new EmbedBuilder()
.WithAuthor(eau => eau.WithName($"{model.username}")
.WithUrl($"https://www.overbuff.com/players/pc/{battletag}")
.WithIconUrl($"{model.avatar}"))
.WithThumbnail(th => th.WithUrl("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 Playtime**").WithValue($"0 hour").WithIsInline(true))
.AddField(fb => fb.WithName("**Quick Playtime**").WithValue($"{model.Playtime.quick}").WithIsInline(true))
.WithOkColor();
await channel.EmbedAsync(embed.Build()).ConfigureAwait(false);
}
}
catch
{
await channel.SendErrorAsync("Found no user! Please check the **Region** and **BattleTag** before trying again.");
}
}
public async Task<OverwatchApiModel.OverwatchPlayer.Data> GetProfile(string region, string battletag)
{
try
{
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);
return model.data;
}
}
catch
{
return null;
}
}
}
}
}

View File

@ -1,6 +1,7 @@
using Discord; using Discord;
using Discord.Commands; using Discord.Commands;
using NadekoBot.Attributes; using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Services; using NadekoBot.Services;
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -36,7 +37,7 @@ namespace NadekoBot.Modules.Searches
{ {
var channel = (ITextChannel)imsg.Channel; var channel = (ITextChannel)imsg.Channel;
await channel.SendMessageAsync(typesStr) await channel.SendConfirmAsync(typesStr)
.ConfigureAwait(false); .ConfigureAwait(false);
} }

View File

@ -1,11 +1,13 @@
using Discord; using Discord;
using Discord.Commands; using Discord.Commands;
using NadekoBot.Attributes; using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Modules.Searches.Models; using NadekoBot.Modules.Searches.Models;
using Newtonsoft.Json; using Newtonsoft.Json;
using NLog; using NLog;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace NadekoBot.Modules.Searches namespace NadekoBot.Modules.Searches
@ -15,15 +17,15 @@ namespace NadekoBot.Modules.Searches
[Group] [Group]
public class PokemonSearchCommands public class PokemonSearchCommands
{ {
private static Dictionary<string, SearchPokemon> pokemons = new Dictionary<string, SearchPokemon>(); private static Dictionary<string, SearchPokemon> pokemons { get; } = new Dictionary<string, SearchPokemon>();
private static Dictionary<string, SearchPokemonAbility> pokemonAbilities = new Dictionary<string, SearchPokemonAbility>(); private static Dictionary<string, SearchPokemonAbility> pokemonAbilities { get; } = new Dictionary<string, SearchPokemonAbility>();
public const string PokemonAbilitiesFile = "data/pokemon/pokemon_abilities.json"; public const string PokemonAbilitiesFile = "data/pokemon/pokemon_abilities7.json";
public const string PokemonListFile = "data/pokemon/pokemon_list.json"; public const string PokemonListFile = "data/pokemon/pokemon_list7.json";
private Logger _log; private static Logger _log { get; }
public PokemonSearchCommands() static PokemonSearchCommands()
{ {
_log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
if (File.Exists(PokemonListFile)) if (File.Exists(PokemonListFile))
@ -52,11 +54,18 @@ namespace NadekoBot.Modules.Searches
{ {
if (kvp.Key.ToUpperInvariant() == pokemon.ToUpperInvariant()) if (kvp.Key.ToUpperInvariant() == pokemon.ToUpperInvariant())
{ {
await channel.SendMessageAsync($"`Stats for \"{kvp.Key}\" pokemon:`\n{kvp.Value}"); var p = kvp.Value;
await 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))
.Build());
return; return;
} }
} }
await channel.SendMessageAsync("`No pokemon found.`"); await channel.SendErrorAsync("No pokemon found.");
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -72,11 +81,15 @@ namespace NadekoBot.Modules.Searches
{ {
if (kvp.Key.ToUpperInvariant() == ability) if (kvp.Key.ToUpperInvariant() == ability)
{ {
await channel.SendMessageAsync($"`Info for \"{kvp.Key}\" ability:`\n{kvp.Value}"); await 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))
.Build()).ConfigureAwait(false);
return; return;
} }
} }
await channel.SendMessageAsync("`No ability found.`"); await channel.SendErrorAsync("No ability found.");
} }
} }
} }

View File

@ -12,6 +12,11 @@ using NadekoBot.Services.Database.Models;
using System.Net.Http; using System.Net.Http;
using Discord.WebSocket; using Discord.WebSocket;
using NadekoBot.Attributes; using NadekoBot.Attributes;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
using NLog;
using NadekoBot.Services.Database;
using NadekoBot.Extensions;
namespace NadekoBot.Modules.Searches namespace NadekoBot.Modules.Searches
{ {
@ -19,128 +24,171 @@ namespace NadekoBot.Modules.Searches
{ {
public class StreamStatus public class StreamStatus
{ {
public StreamStatus(string link, bool isLive, string views)
{
Link = link;
IsLive = isLive;
Views = views;
}
public bool IsLive { get; set; } public bool IsLive { get; set; }
public string Link { get; set; } public string ApiLink { get; set; }
public string Views { get; set; } public string Views { get; set; }
} }
public class HitboxResponse {
public bool Success { get; set; } = true;
[JsonProperty("media_is_live")]
public string MediaIsLive { get; set; }
public bool IsLive => MediaIsLive == "1";
[JsonProperty("media_views")]
public string Views { get; set; }
}
public class TwitchResponse
{
public string Error { get; set; } = null;
public bool IsLive => Stream != null;
public StreamInfo Stream { get; set; }
public class StreamInfo
{
public int Viewers { get; set; }
}
}
public class BeamResponse
{
public string Error { get; set; } = null;
[JsonProperty("online")]
public bool IsLive { get; set; }
public int ViewersCurrent { get; set; }
}
public class StreamNotFoundException : Exception
{
public StreamNotFoundException(string message) : base("Stream '" + message + "' not found.")
{
}
}
[Group] [Group]
public class StreamNotificationCommands public class StreamNotificationCommands
{ {
private Timer checkTimer { get; } private static Timer checkTimer { get; }
private ConcurrentDictionary<string, StreamStatus> oldCachedStatuses = new ConcurrentDictionary<string, StreamStatus>(); private static ConcurrentDictionary<string, StreamStatus> oldCachedStatuses = new ConcurrentDictionary<string, StreamStatus>();
private ConcurrentDictionary<string, StreamStatus> cachedStatuses = new ConcurrentDictionary<string, StreamStatus>(); private static ConcurrentDictionary<string, StreamStatus> cachedStatuses = new ConcurrentDictionary<string, StreamStatus>();
private bool FirstPass { get; set; } = true; private static Logger _log { get; }
public StreamNotificationCommands() private static bool FirstPass { get; set; } = true;
static StreamNotificationCommands()
{ {
_log = NLog.LogManager.GetCurrentClassLogger();
checkTimer = new Timer(async (state) => checkTimer = new Timer(async (state) =>
{ {
oldCachedStatuses = new ConcurrentDictionary<string, StreamStatus>(cachedStatuses); oldCachedStatuses = new ConcurrentDictionary<string, StreamStatus>(cachedStatuses);
cachedStatuses = new ConcurrentDictionary<string, StreamStatus>(); cachedStatuses.Clear();
try
{
IEnumerable<FollowedStream> streams; IEnumerable<FollowedStream> streams;
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
streams = uow.GuildConfigs.GetAllFollowedStreams(); streams = uow.GuildConfigs.GetAllFollowedStreams();
} }
foreach (var stream in streams)
await Task.WhenAll(streams.Select(async fs =>
{ {
StreamStatus data;
try try
{ {
data = await GetStreamStatus(stream).ConfigureAwait(false); var newStatus = await GetStreamStatus(fs).ConfigureAwait(false);
if (data == null) if (FirstPass)
{
return; return;
} }
catch
{
continue;
}
StreamStatus oldData; StreamStatus oldStatus;
oldCachedStatuses.TryGetValue(data.Link, out oldData); if (oldCachedStatuses.TryGetValue(newStatus.ApiLink, out oldStatus) &&
oldStatus.IsLive != newStatus.IsLive)
if (oldData == null || data.IsLive != oldData.IsLive)
{ {
if (FirstPass) var server = NadekoBot.Client.GetGuild(fs.GuildId);
continue; var channel = server?.GetTextChannel(fs.ChannelId);
var server = NadekoBot.Client.GetGuild(stream.GuildId);
var channel = server?.GetTextChannel(stream.ChannelId);
if (channel == null) if (channel == null)
continue; return;
var msg = $"`{stream.Username}`'s stream is now " + try
$"**{(data.IsLive ? "ONLINE" : "OFFLINE")}** with " + {
$"**{data.Views}** viewers."; var msg = await channel.EmbedAsync(fs.GetEmbed(newStatus).Build()).ConfigureAwait(false);
if (data.IsLive) if (!newStatus.IsLive)
if (stream.Type == FollowedStream.FollowedStreamType.Hitbox) msg.DeleteAfter(60);
msg += $"\n`Here is the Link:`【 http://www.hitbox.tv/{stream.Username}/ 】";
else if (stream.Type == FollowedStream.FollowedStreamType.Twitch)
msg += $"\n`Here is the Link:`【 http://www.twitch.tv/{stream.Username}/ 】";
else if (stream.Type == FollowedStream.FollowedStreamType.Beam)
msg += $"\n`Here is the Link:`【 http://www.beam.pro/{stream.Username}/ 】";
try { await channel.SendMessageAsync(msg).ConfigureAwait(false); } catch { }
}
}
FirstPass = false;
} }
catch { } catch { }
}
}
catch { }
}));
FirstPass = false;
}, null, TimeSpan.Zero, TimeSpan.FromSeconds(60)); }, null, TimeSpan.Zero, TimeSpan.FromSeconds(60));
} }
private async Task<StreamStatus> GetStreamStatus(FollowedStream stream, bool checkCache = true) private static async Task<StreamStatus> GetStreamStatus(FollowedStream stream, bool checkCache = true)
{ {
bool isLive;
string response; string response;
JObject data;
StreamStatus result; StreamStatus result;
switch (stream.Type) switch (stream.Type)
{ {
case FollowedStream.FollowedStreamType.Hitbox: case FollowedStream.FollowedStreamType.Hitbox:
var hitboxUrl = $"https://api.hitbox.tv/media/status/{stream.Username}"; 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; return result;
using (var http = new HttpClient()) using (var http = new HttpClient())
{ {
response = await http.GetStringAsync(hitboxUrl).ConfigureAwait(false); response = await http.GetStringAsync(hitboxUrl).ConfigureAwait(false);
} }
data = JObject.Parse(response); var hbData = JsonConvert.DeserializeObject<HitboxResponse>(response);
isLive = data["media_is_live"].ToString() == "1"; if (!hbData.Success)
result = new StreamStatus(hitboxUrl, isLive, data["media_views"].ToString()); throw new StreamNotFoundException($"{stream.Username} [{stream.Type}]");
cachedStatuses.TryAdd(hitboxUrl, result); result = new StreamStatus()
{
IsLive = hbData.IsLive,
ApiLink = hitboxUrl,
Views = hbData.Views
};
cachedStatuses.AddOrUpdate(hitboxUrl, result, (key, old) => result);
return result; return result;
case FollowedStream.FollowedStreamType.Twitch: case FollowedStream.FollowedStreamType.Twitch:
var twitchUrl = $"https://api.twitch.tv/kraken/streams/{Uri.EscapeUriString(stream.Username)}?client_id=67w6z9i09xv2uoojdm9l0wsyph4hxo6"; 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; return result;
using (var http = new HttpClient()) using (var http = new HttpClient())
{ {
response = await http.GetStringAsync(twitchUrl).ConfigureAwait(false); response = await http.GetStringAsync(twitchUrl).ConfigureAwait(false);
} }
data = JObject.Parse(response); var twData = JsonConvert.DeserializeObject<TwitchResponse>(response);
isLive = !string.IsNullOrWhiteSpace(data["stream"].ToString()); if (twData.Error != null)
result = new StreamStatus(twitchUrl, isLive, isLive ? data["stream"]["viewers"].ToString() : "0"); {
cachedStatuses.TryAdd(twitchUrl, result); throw new StreamNotFoundException($"{stream.Username} [{stream.Type}]");
}
result = new StreamStatus()
{
IsLive = twData.IsLive,
ApiLink = twitchUrl,
Views = twData.Stream?.Viewers.ToString() ?? "0"
};
cachedStatuses.AddOrUpdate(twitchUrl, result, (key, old) => result);
return result; return result;
case FollowedStream.FollowedStreamType.Beam: case FollowedStream.FollowedStreamType.Beam:
var beamUrl = $"https://beam.pro/api/v1/channels/{stream.Username}"; 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; return result;
using (var http = new HttpClient()) using (var http = new HttpClient())
{ {
response = await http.GetStringAsync(beamUrl).ConfigureAwait(false); response = await http.GetStringAsync(beamUrl).ConfigureAwait(false);
} }
data = JObject.Parse(response);
isLive = data["online"].ToObject<bool>() == true; var bmData = JsonConvert.DeserializeObject<BeamResponse>(response);
result = new StreamStatus(beamUrl, isLive, data["viewersCurrent"].ToString()); if (bmData.Error != null)
cachedStatuses.TryAdd(beamUrl, result); throw new StreamNotFoundException($"{stream.Username} [{stream.Type}]");
result = new StreamStatus()
{
IsLive = bmData.IsLive,
ApiLink = beamUrl,
Views = bmData.ViewersCurrent.ToString()
};
cachedStatuses.AddOrUpdate(beamUrl, result, (key, old) => result);
return result; return result;
default: default:
break; break;
@ -178,12 +226,15 @@ namespace NadekoBot.Modules.Searches
IEnumerable<FollowedStream> streams; IEnumerable<FollowedStream> streams;
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
streams = uow.GuildConfigs.For(channel.Guild.Id).FollowedStreams; streams = uow.GuildConfigs
.For(channel.Guild.Id,
set => set.Include(gc => gc.FollowedStreams))
.FollowedStreams;
} }
if (!streams.Any()) if (!streams.Any())
{ {
await channel.SendMessageAsync("You are not following any streams on this server.").ConfigureAwait(false); await channel.SendConfirmAsync("You are not following any streams on this server.").ConfigureAwait(false);
return; return;
} }
@ -192,36 +243,39 @@ namespace NadekoBot.Modules.Searches
return $"`{snc.Username}`'s stream on **{channel.Guild.GetTextChannel(snc.ChannelId)?.Name}** channel. 【`{snc.Type.ToString()}`】"; return $"`{snc.Username}`'s stream on **{channel.Guild.GetTextChannel(snc.ChannelId)?.Name}** channel. 【`{snc.Type.ToString()}`】";
})); }));
await channel.SendMessageAsync($"You are following **{streams.Count()}** streams on this server.\n\n" + text).ConfigureAwait(false); await channel.SendConfirmAsync($"You are following **{streams.Count()}** streams on this server.\n\n" + text).ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[RequirePermission(GuildPermission.ManageMessages)] [RequirePermission(GuildPermission.ManageMessages)]
public async Task RemoveStream(IUserMessage msg, [Remainder] string username) public async Task RemoveStream(IUserMessage msg, FollowedStream.FollowedStreamType type, [Remainder] string username)
{ {
var channel = (ITextChannel)msg.Channel; var channel = (ITextChannel)msg.Channel;
username = username.ToLowerInvariant().Trim(); username = username.ToLowerInvariant().Trim();
FollowedStream toRemove; var fs = new FollowedStream()
{
ChannelId = channel.Id,
Username = username,
Type = type
};
bool removed;
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
var config = uow.GuildConfigs.For(channel.Guild.Id); var config = uow.GuildConfigs.For(channel.Guild.Id, set => set.Include(gc => gc.FollowedStreams));
var streams = config.FollowedStreams; removed = config.FollowedStreams.Remove(fs);
toRemove = streams.Where(fs => fs.ChannelId == channel.Id && fs.Username.ToLowerInvariant() == username).FirstOrDefault(); if (removed)
if (toRemove != null) await uow.CompleteAsync().ConfigureAwait(false);
{
config.FollowedStreams = new HashSet<FollowedStream>(streams.Except(new[] { toRemove }));
await uow.CompleteAsync();
} }
} if (!removed)
if (toRemove == null)
{ {
await channel.SendMessageAsync(":anger: No such stream.").ConfigureAwait(false); await channel.SendErrorAsync("No such stream.").ConfigureAwait(false);
return; return;
} }
await channel.SendMessageAsync($":ok: Removed `{toRemove.Username}`'s stream ({toRemove.Type}) from notifications.").ConfigureAwait(false); await channel.SendConfirmAsync($"Removed `{username}`'s stream ({type}) from notifications.").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -238,69 +292,86 @@ namespace NadekoBot.Modules.Searches
var streamStatus = (await GetStreamStatus(new FollowedStream var streamStatus = (await GetStreamStatus(new FollowedStream
{ {
Username = stream, Username = stream,
Type = platform Type = platform,
})); }));
if (streamStatus.IsLive) if (streamStatus.IsLive)
{ {
await channel.SendMessageAsync($"`Streamer {username} is online with {streamStatus.Views} viewers.`"); await channel.SendConfirmAsync($"Streamer {username} is online with {streamStatus.Views} viewers.");
} }
else else
{ {
await channel.SendMessageAsync($"`Streamer {username} is offline.`"); await channel.SendConfirmAsync($"Streamer {username} is offline.");
} }
} }
catch catch
{ {
await channel.SendMessageAsync("No channel found."); await channel.SendErrorAsync("No channel found.");
} }
} }
private 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(); username = username.ToLowerInvariant().Trim();
var stream = new FollowedStream var fs = new FollowedStream
{ {
GuildId = channel.Guild.Id, GuildId = channel.Guild.Id,
ChannelId = channel.Id, ChannelId = channel.Id,
Username = username, Username = username,
Type = type, Type = type,
}; };
bool exists;
using (var uow = DbHandler.UnitOfWork()) StreamStatus status;
{
exists = uow.GuildConfigs.For(channel.Guild.Id).FollowedStreams.Where(fs => fs.ChannelId == channel.Id && fs.Username.ToLowerInvariant().Trim() == username).Any();
}
if (exists)
{
await channel.SendMessageAsync($":anger: I am already following `{username}` ({type}) stream on this channel.").ConfigureAwait(false);
return;
}
StreamStatus data;
try try
{ {
data = await GetStreamStatus(stream).ConfigureAwait(false); status = await GetStreamStatus(fs).ConfigureAwait(false);
} }
catch catch
{ {
await channel.SendMessageAsync(":anger: Stream probably doesn't exist.").ConfigureAwait(false); await channel.SendErrorAsync("Stream probably doesn't exist.").ConfigureAwait(false);
return; return;
} }
var msg = $"Stream is currently **{(data.IsLive ? "ONLINE" : "OFFLINE")}** with **{data.Views}** viewers";
if (data.IsLive)
if (type == FollowedStream.FollowedStreamType.Hitbox)
msg += $"\n`Here is the Link:`【 http://www.hitbox.tv/{stream.Username}/ 】";
else if (type == FollowedStream.FollowedStreamType.Twitch)
msg += $"\n`Here is the Link:`【 http://www.twitch.tv/{stream.Username}/ 】";
else if (type == FollowedStream.FollowedStreamType.Beam)
msg += $"\n`Here is the Link:`【 https://beam.pro/{stream.Username}/ 】";
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
uow.GuildConfigs.For(channel.Guild.Id).FollowedStreams.Add(stream); uow.GuildConfigs.For(channel.Guild.Id, set => set.Include(gc => gc.FollowedStreams))
await uow.CompleteAsync(); .FollowedStreams
.Add(fs);
await uow.CompleteAsync().ConfigureAwait(false);
} }
msg = $":ok: I will notify this channel when status changes.\n{msg}"; await channel.EmbedAsync(fs.GetEmbed(status).Build(), $"🆗 I will notify this channel when status changes.").ConfigureAwait(false);
await channel.SendMessageAsync(msg).ConfigureAwait(false);
} }
} }
} }
public static class FollowedStreamExtensions
{
public static EmbedBuilder GetEmbed(this FollowedStream fs, Searches.StreamStatus status)
{
var embed = new EmbedBuilder().WithTitle(fs.Username)
.WithUrl(fs.GetLink())
.AddField(efb => efb.WithName("Status")
.WithValue(status.IsLive ? "Online" : "Offline")
.WithIsInline(true))
.AddField(efb => efb.WithName("Viewers")
.WithValue(status.IsLive ? status.Views : "-")
.WithIsInline(true))
.AddField(efb => efb.WithName("Platform")
.WithValue(fs.Type.ToString())
.WithIsInline(true))
.WithColor(status.IsLive ? NadekoBot.OkColor : NadekoBot.ErrorColor);
return embed;
}
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)
return $"http://www.twitch.tv/{fs.Username}/";
else if (fs.Type == FollowedStream.FollowedStreamType.Beam)
return $"https://beam.pro/{fs.Username}/";
else
return "??";
}
}
} }

View File

@ -28,18 +28,17 @@ namespace NadekoBot.Modules.Searches
TranslatedChannels = new ConcurrentDictionary<ulong, bool>(); TranslatedChannels = new ConcurrentDictionary<ulong, bool>();
UserLanguages = new ConcurrentDictionary<UserChannelPair, string>(); UserLanguages = new ConcurrentDictionary<UserChannelPair, string>();
NadekoBot.Client.MessageReceived += (msg) => NadekoBot.Client.MessageReceived += async (msg) =>
{
try
{ {
var umsg = msg as IUserMessage; var umsg = msg as IUserMessage;
if (umsg == null) if (umsg == null)
return Task.CompletedTask; return;
bool autoDelete; bool autoDelete;
if (!TranslatedChannels.TryGetValue(umsg.Channel.Id, out autoDelete)) if (!TranslatedChannels.TryGetValue(umsg.Channel.Id, out autoDelete))
return Task.CompletedTask; return;
var t = Task.Run(async () =>
{
var key = new UserChannelPair() var key = new UserChannelPair()
{ {
UserId = umsg.Author.Id, UserId = umsg.Author.Id,
@ -50,18 +49,13 @@ namespace NadekoBot.Modules.Searches
if (!UserLanguages.TryGetValue(key, out langs)) if (!UserLanguages.TryGetValue(key, out langs))
return; return;
try
{
var text = await TranslateInternal(umsg, langs, umsg.Resolve(UserMentionHandling.Ignore), true) var text = await TranslateInternal(umsg, langs, umsg.Resolve(UserMentionHandling.Ignore), true)
.ConfigureAwait(false); .ConfigureAwait(false);
if (autoDelete) if (autoDelete)
try { await umsg.DeleteAsync().ConfigureAwait(false); } catch { } try { await umsg.DeleteAsync().ConfigureAwait(false); } catch { }
await umsg.Channel.SendMessageAsync($"{umsg.Author.Mention} `:` "+text.Replace("<@ ", "<@").Replace("<@! ", "<@!")).ConfigureAwait(false); await umsg.Channel.SendConfirmAsync($"{umsg.Author.Mention} `:` " + text.Replace("<@ ", "<@").Replace("<@! ", "<@!")).ConfigureAwait(false);
} }
catch { } catch { }
});
return Task.CompletedTask;
}; };
} }
@ -75,12 +69,11 @@ namespace NadekoBot.Modules.Searches
{ {
await umsg.Channel.TriggerTypingAsync().ConfigureAwait(false); await umsg.Channel.TriggerTypingAsync().ConfigureAwait(false);
var translation = await TranslateInternal(umsg, langs, text); var translation = await TranslateInternal(umsg, langs, text);
await channel.SendMessageAsync(translation).ConfigureAwait(false); await channel.SendConfirmAsync("Translation " + langs, translation).ConfigureAwait(false);
} }
catch catch
{ {
await channel.SendMessageAsync("Bad input format, or something went wrong...").ConfigureAwait(false); await channel.SendErrorAsync("Bad input format, or something went wrong...").ConfigureAwait(false);
} }
} }
@ -94,7 +87,7 @@ namespace NadekoBot.Modules.Searches
text = text?.Trim(); text = text?.Trim();
if (string.IsNullOrWhiteSpace(text)) if (string.IsNullOrWhiteSpace(text))
throw new ArgumentException(); throw new ArgumentException();
return await GoogleTranslator.Instance.Translate(text, from, to).ConfigureAwait(false); return (await GoogleTranslator.Instance.Translate(text, from, to).ConfigureAwait(false)).SanitizeMentions();
} }
public enum AutoDeleteAutoTranslate public enum AutoDeleteAutoTranslate
@ -114,19 +107,19 @@ namespace NadekoBot.Modules.Searches
if (autoDelete == AutoDeleteAutoTranslate.Del) if (autoDelete == AutoDeleteAutoTranslate.Del)
{ {
TranslatedChannels.AddOrUpdate(channel.Id, true, (key, val) => true); TranslatedChannels.AddOrUpdate(channel.Id, true, (key, val) => true);
try { await channel.SendMessageAsync("`Started automatic translation of messages on this channel. User messages will be auto-deleted.`").ConfigureAwait(false); } catch { } try { await channel.SendConfirmAsync("Started automatic translation of messages on this channel. User messages will be auto-deleted.").ConfigureAwait(false); } catch { }
return; return;
} }
bool throwaway; bool throwaway;
if (TranslatedChannels.TryRemove(channel.Id, out throwaway)) if (TranslatedChannels.TryRemove(channel.Id, out throwaway))
{ {
try { await channel.SendMessageAsync("`Stopped automatic translation of messages on this channel.`").ConfigureAwait(false); } catch { } try { await channel.SendConfirmAsync("Stopped automatic translation of messages on this channel.").ConfigureAwait(false); } catch { }
return; return;
} }
else if (TranslatedChannels.TryAdd(channel.Id, autoDelete == AutoDeleteAutoTranslate.Del)) else if (TranslatedChannels.TryAdd(channel.Id, autoDelete == AutoDeleteAutoTranslate.Del))
{ {
try { await channel.SendMessageAsync("`Started automatic translation of messages on this channel.`").ConfigureAwait(false); } catch { } try { await channel.SendConfirmAsync("Started automatic translation of messages on this channel.").ConfigureAwait(false); } catch { }
} }
} }
@ -145,7 +138,7 @@ namespace NadekoBot.Modules.Searches
if (string.IsNullOrWhiteSpace(langs)) if (string.IsNullOrWhiteSpace(langs))
{ {
if (UserLanguages.TryRemove(ucp, out langs)) if (UserLanguages.TryRemove(ucp, out langs))
await channel.SendMessageAsync($"{msg.Author.Mention}'s auto-translate language has been removed.").ConfigureAwait(false); await channel.SendConfirmAsync($"{msg.Author.Mention}'s auto-translate language has been removed.").ConfigureAwait(false);
return; return;
} }
@ -157,13 +150,13 @@ namespace NadekoBot.Modules.Searches
if (!GoogleTranslator.Instance.Languages.Contains(from) || !GoogleTranslator.Instance.Languages.Contains(to)) if (!GoogleTranslator.Instance.Languages.Contains(from) || !GoogleTranslator.Instance.Languages.Contains(to))
{ {
try { await channel.SendMessageAsync("`Invalid source and/or target Language.`").ConfigureAwait(false); } catch { } try { await channel.SendErrorAsync("Invalid source and/or target language.").ConfigureAwait(false); } catch { }
return; return;
} }
UserLanguages.AddOrUpdate(ucp, langs, (key, val) => langs); UserLanguages.AddOrUpdate(ucp, langs, (key, val) => langs);
await channel.SendMessageAsync(":ok:").ConfigureAwait(false); await channel.SendConfirmAsync($"Your auto-translate language has been set to {from}>{to}").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]

View File

@ -1,6 +1,7 @@
using Discord; using Discord;
using Discord.Commands; using Discord.Commands;
using NadekoBot.Attributes; using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Services; using NadekoBot.Services;
using Newtonsoft.Json; using Newtonsoft.Json;
using System.Net.Http; using System.Net.Http;
@ -55,12 +56,17 @@ namespace NadekoBot.Modules.Searches
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 comic = JsonConvert.DeserializeObject<XkcdComic>(res);
var sent = await channel.SendMessageAsync($"{msg.Author.Mention} " + comic.ToString()) var embed = new EmbedBuilder().WithOkColor()
.WithImage(eib => eib.WithUrl(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));
var sent = await channel.EmbedAsync(embed.Build())
.ConfigureAwait(false); .ConfigureAwait(false);
await Task.Delay(10000).ConfigureAwait(false); await Task.Delay(10000).ConfigureAwait(false);
await sent.ModifyAsync(m => m.Content = sent.Content + $"\n`Alt:` {comic.Alt}"); await sent.ModifyAsync(m => m.Embed = embed.AddField(efb => efb.WithName("Alt").WithValue(comic.Alt.ToString()).WithIsInline(false)).Build());
} }
} }
} }

View File

@ -17,43 +17,45 @@ using ImageSharp;
using NadekoBot.Extensions; using NadekoBot.Extensions;
using System.IO; using System.IO;
using NadekoBot.Modules.Searches.Commands.OMDB; 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;
namespace NadekoBot.Modules.Searches namespace NadekoBot.Modules.Searches
{ {
[NadekoModule("Searches", "~")] [NadekoModule("Searches", "~")]
public partial class Searches : DiscordModule public partial class Searches : DiscordModule
{ {
private IGoogleApiService _google { get; }
public Searches(ILocalization loc, CommandService cmds, ShardedDiscordClient client, IGoogleApiService youtube) : base(loc, cmds, client)
{
_google = youtube;
}
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task Weather(IUserMessage umsg, string city, string country) public async Task Weather(IUserMessage umsg, [Remainder] string query)
{ {
var channel = (ITextChannel)umsg.Channel; var channel = (ITextChannel)umsg.Channel;
city = city.Replace(" ", ""); if (string.IsNullOrWhiteSpace(query))
country = city.Replace(" ", ""); return;
string response; string response;
using (var http = new HttpClient()) using (var http = new HttpClient())
response = await http.GetStringAsync($"http://api.ninetales.us/nadekobot/weather/?city={city}&country={country}").ConfigureAwait(false); response = await http.GetStringAsync($"http://api.openweathermap.org/data/2.5/weather?q={query}&appid=42cd627dd60debf25a5739e50a217d74&units=metric").ConfigureAwait(false);
var obj = JObject.Parse(response)["weather"]; var data = JsonConvert.DeserializeObject<WeatherData>(response);
var embed = new EmbedBuilder() var embed = new EmbedBuilder()
.AddField(fb => fb.WithName("🌍 Location").WithValue($"{obj["target"]}").WithIsInline(true)) .AddField(fb => fb.WithName("🌍 **Location**").WithValue(data.name + ", " + data.sys.country).WithIsInline(true))
.AddField(fb => fb.WithName("📏 Lat,Long").WithValue($"{obj["latitude"]}, {obj["longitude"]}").WithIsInline(true)) .AddField(fb => fb.WithName("📏 **Lat,Long**").WithValue($"{data.coord.lat}, {data.coord.lon}").WithIsInline(true))
.AddField(fb => fb.WithName("☁ Condition").WithValue($"{obj["condition"]}").WithIsInline(true)) .AddField(fb => fb.WithName("☁ **Condition**").WithValue(String.Join(", ", data.weather.Select(w => w.main))).WithIsInline(true))
.AddField(fb => fb.WithName("😓 Humidity").WithValue($"{obj["humidity"]}%").WithIsInline(true)) .AddField(fb => fb.WithName("😓 **Humidity**").WithValue($"{data.main.humidity}%").WithIsInline(true))
.AddField(fb => fb.WithName("💨 Wind Speed").WithValue($"{obj["windspeedk"]}km/h / {obj["windspeedm"]}mph").WithIsInline(true)) .AddField(fb => fb.WithName("💨 **Wind Speed**").WithValue(data.wind.speed + " km/h").WithIsInline(true))
.AddField(fb => fb.WithName("🌡 Temperature").WithValue($"{obj["centigrade"]}°C / {obj["fahrenheit"]}°F").WithIsInline(true)) .AddField(fb => fb.WithName("🌡 **Temperature**").WithValue(data.main.temp + "°C").WithIsInline(true))
.AddField(fb => fb.WithName("🔆 Feels like").WithValue($"{obj["feelscentigrade"]}°C / {obj["feelsfahrenheit"]}°F").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").WithValue($"{obj["sunrise"]}").WithIsInline(true)) .AddField(fb => fb.WithName("🌄 **Sunrise (utc)**").WithValue($"{data.sys.sunrise.ToUnixTimestamp():HH:mm}").WithIsInline(true))
.AddField(fb => fb.WithName("🌇 Sunset").WithValue($"{obj["sunset"]}").WithIsInline(true)) .AddField(fb => fb.WithName("🌇 **Sunset (utc)**").WithValue($"{data.sys.sunset.ToUnixTimestamp():HH:mm}").WithIsInline(true))
.WithColor(NadekoBot.OkColor); .WithOkColor()
.WithFooter(efb => efb.WithText("Powered by http://openweathermap.org"));
await channel.EmbedAsync(embed.Build()).ConfigureAwait(false); await channel.EmbedAsync(embed.Build()).ConfigureAwait(false);
} }
@ -63,13 +65,16 @@ namespace NadekoBot.Modules.Searches
{ {
var channel = (ITextChannel)umsg.Channel; var channel = (ITextChannel)umsg.Channel;
if (!(await ValidateQuery(channel, query).ConfigureAwait(false))) return; if (!(await ValidateQuery(channel, query).ConfigureAwait(false))) return;
var result = (await _google.GetVideosByKeywordsAsync(query, 1)).FirstOrDefault(); var result = (await NadekoBot.Google.GetVideosByKeywordsAsync(query, 1)).FirstOrDefault();
if (string.IsNullOrWhiteSpace(result)) if (string.IsNullOrWhiteSpace(result))
{ {
await channel.SendMessageAsync("No results found for that query."); await channel.SendErrorAsync("No results found for that query.").ConfigureAwait(false);
return; return;
} }
await channel.SendMessageAsync(result).ConfigureAwait(false); await channel.SendMessageAsync(result).ConfigureAwait(false);
//await 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] [NadekoCommand, Usage, Description, Aliases]
@ -84,10 +89,10 @@ namespace NadekoBot.Modules.Searches
var movie = await OmdbProvider.FindMovie(query); var movie = await OmdbProvider.FindMovie(query);
if (movie == null) if (movie == null)
{ {
await channel.SendMessageAsync("Failed to find that movie.").ConfigureAwait(false); await channel.SendErrorAsync("Failed to find that movie.").ConfigureAwait(false);
return; return;
} }
await channel.SendMessageAsync(movie.ToString()).ConfigureAwait(false); await channel.EmbedAsync(movie.GetEmbed()).ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -97,9 +102,8 @@ namespace NadekoBot.Modules.Searches
var channel = (ITextChannel)umsg.Channel; var channel = (ITextChannel)umsg.Channel;
using (var http = new HttpClient()) using (var http = new HttpClient())
{ {
await channel.SendMessageAsync(JObject.Parse( var res = JObject.Parse(await http.GetStringAsync("http://www.random.cat/meow").ConfigureAwait(false));
await http.GetStringAsync("http://www.random.cat/meow").ConfigureAwait(false))["file"].ToString()) await channel.SendMessageAsync(res["file"].ToString()).ConfigureAwait(false);
.ConfigureAwait(false);
} }
} }
@ -110,7 +114,8 @@ namespace NadekoBot.Modules.Searches
var channel = (ITextChannel)umsg.Channel; var channel = (ITextChannel)umsg.Channel;
using (var http = new HttpClient()) using (var http = new HttpClient())
{ {
await channel.SendMessageAsync("http://random.dog/" + await http.GetStringAsync("http://random.dog/woof").ConfigureAwait(false)).ConfigureAwait(false); await channel.SendMessageAsync("http://random.dog/" + await http.GetStringAsync("http://random.dog/woof")
.ConfigureAwait(false)).ConfigureAwait(false);
} }
} }
@ -135,11 +140,12 @@ namespace NadekoBot.Modules.Searches
{ {
if (exception.Message.Contains("403 (Forbidden)")) if (exception.Message.Contains("403 (Forbidden)"))
{ {
await channel.SendMessageAsync("Daily limit reached!"); await channel.SendErrorAsync("Daily limit reached!");
} }
else else
{ {
await channel.SendMessageAsync("Something went wrong."); await channel.SendErrorAsync("Something went wrong.");
_log.Error(exception);
} }
} }
} }
@ -167,11 +173,12 @@ namespace NadekoBot.Modules.Searches
{ {
if (exception.Message.Contains("403 (Forbidden)")) if (exception.Message.Contains("403 (Forbidden)"))
{ {
await channel.SendMessageAsync("Daily limit reached!"); await channel.SendErrorAsync("Daily limit reached!");
} }
else else
{ {
await channel.SendMessageAsync("Something went wrong."); await channel.SendErrorAsync("Something went wrong.");
_log.Error(exception);
} }
} }
} }
@ -186,7 +193,7 @@ namespace NadekoBot.Modules.Searches
if (string.IsNullOrWhiteSpace(ffs)) if (string.IsNullOrWhiteSpace(ffs))
return; return;
await channel.SendMessageAsync(await _google.ShortenUrl($"<http://lmgtfy.com/?q={ Uri.EscapeUriString(ffs) }>")) await channel.SendConfirmAsync(await NadekoBot.Google.ShortenUrl($"<http://lmgtfy.com/?q={ Uri.EscapeUriString(ffs) }>"))
.ConfigureAwait(false); .ConfigureAwait(false);
} }
@ -197,21 +204,78 @@ namespace NadekoBot.Modules.Searches
if (string.IsNullOrWhiteSpace(arg)) if (string.IsNullOrWhiteSpace(arg))
return; return;
await msg.Channel.SendMessageAsync(await NadekoBot.Google.ShortenUrl(arg).ConfigureAwait(false)); var shortened = await NadekoBot.Google.ShortenUrl(arg).ConfigureAwait(false);
if (shortened == arg)
{
await msg.Channel.SendErrorAsync("Failed to shorten that url.").ConfigureAwait(false);
} }
await msg.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
.AddField(efb => efb.WithName("Original Url")
.WithValue($"<{arg}>"))
.AddField(efb => efb.WithName("Short Url")
.WithValue($"<{shortened}>"))
.Build())
.ConfigureAwait(false);
}
//private readonly Regex googleSearchRegex = new Regex(@"<h3 class=""r""><a href=""(?:\/url?q=)?(?<link>.*?)"".*?>(?<title>.*?)<\/a>.*?class=""st"">(?<text>.*?)<\/span>", RegexOptions.Compiled);
//private readonly Regex htmlReplace = new Regex(@"(?:<b>(.*?)<\/b>|<em>(.*?)<\/em>)", RegexOptions.Compiled);
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task Google(IUserMessage umsg, [Remainder] string terms = null) public async Task Google(IUserMessage umsg, [Remainder] string terms = null)
{ {
var channel = (ITextChannel)umsg.Channel; var channel = (ITextChannel)umsg.Channel;
terms = terms?.Trim(); terms = terms?.Trim();
if (string.IsNullOrWhiteSpace(terms)) if (string.IsNullOrWhiteSpace(terms))
return; return;
await channel.SendMessageAsync($"https://google.com/search?q={ WebUtility.UrlEncode(terms).Replace(' ', '+') }")
terms = WebUtility.UrlEncode(terms).Replace(' ', '+');
var fullQueryLink = $"https://www.google.com/search?q={ terms }&gws_rd=cr,ssl";
var config = Configuration.Default.WithDefaultLoader();
var document = await BrowsingContext.New(config).OpenAsync(fullQueryLink);
var elems = document.QuerySelectorAll("div.g");
var resultsElem = document.QuerySelectorAll("#resultStats").FirstOrDefault();
var totalResults = resultsElem?.TextContent;
//var time = resultsElem.Children.FirstOrDefault()?.TextContent
//^ this doesn't work for some reason, <nobr> is completely missing in parsed collection
if (!elems.Any())
return;
var results = elems.Select<IElement, GoogleSearchResult?>(elem =>
{
var aTag = (elem.Children.FirstOrDefault().Children.FirstOrDefault() as IHtmlAnchorElement); // <h3> -> <a>
var href = aTag?.Href;
var name = aTag?.TextContent;
if (href == null || name == null)
return null;
var txt = elem.QuerySelectorAll(".st").FirstOrDefault()?.TextContent;
if (txt == null)
return null;
return new GoogleSearchResult(name, href, txt);
}).Where(x => x != null).Take(5);
var embed = new EmbedBuilder()
.WithOkColor()
.WithAuthor(eab => eab.WithName("Search For: " + terms.TrimTo(50))
.WithUrl(fullQueryLink)
.WithIconUrl("http://i.imgur.com/G46fm8J.png"))
.WithTitle(umsg.Author.Mention)
.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); .ConfigureAwait(false);
await channel.EmbedAsync(embed.WithDescription(String.Concat(desc)).Build()).ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -222,7 +286,7 @@ namespace NadekoBot.Modules.Searches
var arg = name; var arg = name;
if (string.IsNullOrWhiteSpace(arg)) if (string.IsNullOrWhiteSpace(arg))
{ {
await channel.SendMessageAsync("💢 `Please enter a card name to search for.`").ConfigureAwait(false); await channel.SendErrorAsync("Please enter a card name to search for.").ConfigureAwait(false);
return; return;
} }
@ -238,18 +302,26 @@ namespace NadekoBot.Modules.Searches
var items = JArray.Parse(response).Shuffle().ToList(); var items = JArray.Parse(response).Shuffle().ToList();
if (items == null) if (items == null)
throw new KeyNotFoundException("Cannot find a card by that name"); throw new KeyNotFoundException("Cannot find a card by that name");
var msg = $@"```css var item = items[0];
[☕ Magic The Gathering]: {items[0]["name"].ToString()} var storeUrl = await NadekoBot.Google.ShortenUrl(item["store_url"].ToString());
[Store URL]: {await _google.ShortenUrl(items[0]["store_url"].ToString())} var cost = item["cost"].ToString();
[Cost]: {items[0]["cost"].ToString()} var desc = item["text"].ToString();
[Description]: {items[0]["text"].ToString()} var types = String.Join(",\n", item["types"].ToObject<string[]>());
``` var img = item["editions"][0]["image_url"].ToString();
{items[0]["editions"][0]["image_url"].ToString()}"; var embed = new EmbedBuilder().WithOkColor()
await channel.SendMessageAsync(msg).ConfigureAwait(false); .WithTitle(item["name"].ToString())
.WithDescription(desc)
.WithImage(eib => eib.WithUrl(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("Store Url").WithValue(await NadekoBot.Google.ShortenUrl(items[0]["store_url"].ToString())).WithIsInline(true));
await channel.EmbedAsync(embed.Build()).ConfigureAwait(false);
} }
catch catch
{ {
await channel.SendMessageAsync($"💢 Error could not find the card {arg}").ConfigureAwait(false); await channel.SendErrorAsync($"Error could not find the card '{arg}'.").ConfigureAwait(false);
} }
} }
} }
@ -262,13 +334,13 @@ namespace NadekoBot.Modules.Searches
var arg = name; var arg = name;
if (string.IsNullOrWhiteSpace(arg)) if (string.IsNullOrWhiteSpace(arg))
{ {
await channel.SendMessageAsync("💢 `Please enter a card name to search for.`").ConfigureAwait(false); await channel.SendErrorAsync("Please enter a card name to search for.").ConfigureAwait(false);
return; return;
} }
if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.MashapeKey)) if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.MashapeKey))
{ {
await channel.SendMessageAsync("💢 `Bot owner didn't specify MashapeApiKey. You can't use this functionality.`").ConfigureAwait(false); await channel.SendErrorAsync("Bot owner didn't specify MashapeApiKey. You can't use this functionality.").ConfigureAwait(false);
return; return;
} }
@ -308,7 +380,49 @@ namespace NadekoBot.Modules.Searches
} }
catch (Exception ex) catch (Exception ex)
{ {
await channel.SendMessageAsync($"💢 Error {ex.Message}").ConfigureAwait(false); await channel.SendErrorAsync($"Error occured.").ConfigureAwait(false);
_log.Error(ex);
}
}
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Yodify(IUserMessage umsg, [Remainder] string query = null)
{
var channel = (ITextChannel)umsg.Channel;
if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.MashapeKey))
{
await channel.SendErrorAsync("Bot owner didn't specify MashapeApiKey. You can't use this functionality.").ConfigureAwait(false);
return;
}
var arg = query;
if (string.IsNullOrWhiteSpace(arg))
{
await channel.SendErrorAsync("Please enter a sentence.").ConfigureAwait(false);
return;
}
await umsg.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);
try
{
var embed = new EmbedBuilder()
.WithUrl("http://www.yodaspeak.co.uk/")
.WithAuthor(au => au.WithName("Yoda").WithIconUrl("http://www.yodaspeak.co.uk/yoda-small1.gif"))
.WithDescription(res)
.WithOkColor();
await channel.EmbedAsync(embed.Build()).ConfigureAwait(false);
}
catch
{
await channel.SendErrorAsync("Failed to yodify your sentence.").ConfigureAwait(false);
} }
} }
} }
@ -321,38 +435,78 @@ namespace NadekoBot.Modules.Searches
if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.MashapeKey)) if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.MashapeKey))
{ {
await channel.SendMessageAsync("💢 `Bot owner didn't specify MashapeApiKey. You can't use this functionality.`").ConfigureAwait(false); await channel.SendErrorAsync("Bot owner didn't specify MashapeApiKey. You can't use this functionality.").ConfigureAwait(false);
return; return;
} }
var arg = query; var arg = query;
if (string.IsNullOrWhiteSpace(arg)) if (string.IsNullOrWhiteSpace(arg))
{ {
await channel.SendMessageAsync("💢 `Please enter a search term.`").ConfigureAwait(false); await channel.SendErrorAsync("Please enter a search term.").ConfigureAwait(false);
return; return;
} }
await umsg.Channel.TriggerTypingAsync().ConfigureAwait(false); await umsg.Channel.TriggerTypingAsync().ConfigureAwait(false);
using (var http = new HttpClient()) using (var http = new HttpClient())
{ {
http.DefaultRequestHeaders.Clear(); http.DefaultRequestHeaders.Clear();
http.DefaultRequestHeaders.Add("X-Mashape-Key", NadekoBot.Credentials.MashapeKey); http.DefaultRequestHeaders.Add("Accept", "application/json");
var res = await http.GetStringAsync($"https://mashape-community-urban-dictionary.p.mashape.com/define?term={Uri.EscapeUriString(arg)}").ConfigureAwait(false); var res = await http.GetStringAsync($"http://api.urbandictionary.com/v0/define?term={Uri.EscapeUriString(arg)}").ConfigureAwait(false);
try try
{ {
var items = JObject.Parse(res); var items = JObject.Parse(res);
var sb = new StringBuilder(); var item = items["list"][0];
sb.AppendLine($"`Term:` {items["list"][0]["word"].ToString()}"); var word = item["word"].ToString();
sb.AppendLine($"`Definition:` {items["list"][0]["definition"].ToString()}"); var def = item["definition"].ToString();
sb.Append($"`Link:` <{await _google.ShortenUrl(items["list"][0]["permalink"].ToString()).ConfigureAwait(false)}>"); var link = item["permalink"].ToString();
await channel.SendMessageAsync(sb.ToString()); var embed = new EmbedBuilder().WithOkColor()
.WithUrl(link)
.WithAuthor(eab => eab.WithIconUrl("http://i.imgur.com/nwERwQE.jpg").WithName(word))
.WithDescription(def);
await channel.EmbedAsync(embed.Build()).ConfigureAwait(false);
} }
catch catch
{ {
await channel.SendMessageAsync("💢 Failed finding a definition for that term.").ConfigureAwait(false); await channel.SendErrorAsync("Failed finding a definition for that term.").ConfigureAwait(false);
} }
} }
} }
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Define(IUserMessage msg, [Remainder] string word)
{
var channel = (ITextChannel)msg.Channel;
if (string.IsNullOrWhiteSpace(word))
return;
using (var http = new HttpClient())
{
var res = await http.GetStringAsync("http://api.pearson.com/v2/dictionaries/entries?headword=" + WebUtility.UrlEncode(word.Trim())).ConfigureAwait(false);
var data = JsonConvert.DeserializeObject<DefineModel>(res);
var sense = data.Results.Where(x => x.Senses != null && x.Senses[0].Definition != null).FirstOrDefault()?.Senses[0];
if (sense?.Definition == null)
return;
string 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)
.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));
await channel.EmbedAsync(embed.Build()).ConfigureAwait(false);
}
}
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task Hashtag(IUserMessage umsg, [Remainder] string query = null) public async Task Hashtag(IUserMessage umsg, [Remainder] string query = null)
@ -362,12 +516,12 @@ namespace NadekoBot.Modules.Searches
var arg = query; var arg = query;
if (string.IsNullOrWhiteSpace(arg)) if (string.IsNullOrWhiteSpace(arg))
{ {
await channel.SendMessageAsync("💢 `Please enter a search term.`").ConfigureAwait(false); await channel.SendErrorAsync("Please enter a search term.").ConfigureAwait(false);
return; return;
} }
if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.MashapeKey)) if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.MashapeKey))
{ {
await channel.SendMessageAsync("💢 `Bot owner didn't specify MashapeApiKey. You can't use this functionality.`").ConfigureAwait(false); await channel.SendErrorAsync("Bot owner didn't specify MashapeApiKey. You can't use this functionality.").ConfigureAwait(false);
return; return;
} }
@ -383,14 +537,20 @@ namespace NadekoBot.Modules.Searches
try try
{ {
var items = JObject.Parse(res); var items = JObject.Parse(res);
var str = $@"`Hashtag:` {items["defs"]["def"]["hashtag"].ToString()} var item = items["defs"]["def"];
`Definition:` {items["defs"]["def"]["text"].ToString()} var hashtag = item["hashtag"].ToString();
`Link:` <{await _google.ShortenUrl(items["defs"]["def"]["uri"].ToString()).ConfigureAwait(false)}>"; var link = item["uri"].ToString();
await channel.SendMessageAsync(str); var desc = item["text"].ToString();
await channel.EmbedAsync(new EmbedBuilder().WithOkColor()
.WithAuthor(eab => eab.WithUrl(link)
.WithIconUrl("http://res.cloudinary.com/urbandictionary/image/upload/a_exif,c_fit,h_200,w_200/v1394975045/b8oszuu3tbq7ebyo7vo1.jpg")
.WithName(query))
.WithDescription(desc)
.Build());
} }
catch catch
{ {
await channel.SendMessageAsync("💢 Failed finding a definition for that tag.").ConfigureAwait(false); await channel.SendErrorAsync("Failed finding a definition for that tag.").ConfigureAwait(false);
} }
} }
@ -404,7 +564,9 @@ namespace NadekoBot.Modules.Searches
var response = await http.GetStringAsync("http://catfacts-api.appspot.com/api/facts").ConfigureAwait(false); var response = await http.GetStringAsync("http://catfacts-api.appspot.com/api/facts").ConfigureAwait(false);
if (response == null) if (response == null)
return; return;
await channel.SendMessageAsync($"🐈 `{JObject.Parse(response)["facts"][0].ToString()}`").ConfigureAwait(false);
var fact = JObject.Parse(response)["facts"][0].ToString();
await channel.SendConfirmAsync("🐈fact", fact).ConfigureAwait(false);
} }
} }
@ -416,7 +578,7 @@ namespace NadekoBot.Modules.Searches
if (usr == null) if (usr == null)
usr = umsg.Author; usr = umsg.Author;
await channel.SendMessageAsync($"https://images.google.com/searchbyimage?image_url={usr.AvatarUrl}").ConfigureAwait(false); await channel.SendConfirmAsync($"https://images.google.com/searchbyimage?image_url={usr.AvatarUrl}").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -428,22 +590,13 @@ namespace NadekoBot.Modules.Searches
if (string.IsNullOrWhiteSpace(imageLink)) if (string.IsNullOrWhiteSpace(imageLink))
return; return;
await channel.SendMessageAsync($"https://images.google.com/searchbyimage?image_url={imageLink}").ConfigureAwait(false); await channel.SendConfirmAsync($"https://images.google.com/searchbyimage?image_url={imageLink}").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task Safebooru(IUserMessage umsg, [Remainder] string tag = null) public Task Safebooru(IUserMessage umsg, [Remainder] string tag = null)
{ => InternalDapiCommand(umsg, tag, DapiSearchType.Safebooru);
var channel = (ITextChannel)umsg.Channel;
tag = tag?.Trim() ?? "";
var link = await GetSafebooruImageLink(tag).ConfigureAwait(false);
if (link == null)
await channel.SendMessageAsync("`No results.`");
else
await channel.SendMessageAsync(link).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
@ -459,7 +612,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 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); var data = JsonConvert.DeserializeObject<WikipediaApiModel>(result);
if (data.Query.Pages[0].Missing) if (data.Query.Pages[0].Missing)
await channel.SendMessageAsync("`That page could not be found.`"); await channel.SendErrorAsync("That page could not be found.");
else else
await channel.SendMessageAsync(data.Query.Pages[0].FullUrl); await channel.SendMessageAsync(data.Query.Pages[0].FullUrl);
} }
@ -499,12 +652,12 @@ namespace NadekoBot.Modules.Searches
str += new NadekoRandom().Next(); str += new NadekoRandom().Next();
foreach (var usr in allUsrsArray) foreach (var usr in allUsrsArray)
{ {
await (await (usr as IGuildUser).CreateDMChannelAsync()).SendMessageAsync(str).ConfigureAwait(false); await (await (usr as IGuildUser).CreateDMChannelAsync()).SendConfirmAsync(str).ConfigureAwait(false);
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine(ex); _log.Error(ex);
} }
} }
@ -517,180 +670,10 @@ namespace NadekoBot.Modules.Searches
var usr = umsg.MentionedUsers.FirstOrDefault(); var usr = umsg.MentionedUsers.FirstOrDefault();
if (usr == null) if (usr == null)
{ {
await channel.SendMessageAsync("Invalid user specified.").ConfigureAwait(false); await channel.SendErrorAsync("Invalid user specified.").ConfigureAwait(false);
return; return;
} }
await channel.SendMessageAsync(await _google.ShortenUrl(usr.AvatarUrl).ConfigureAwait(false)).ConfigureAwait(false); await channel.SendMessageAsync(await NadekoBot.Google.ShortenUrl(usr.AvatarUrl).ConfigureAwait(false)).ConfigureAwait(false);
}
public static async Task<string> GetSafebooruImageLink(string tag)
{
var rng = new NadekoRandom();
var url =
$"http://safebooru.org/index.php?page=dapi&s=post&q=index&limit=100&tags={tag.Replace(" ", "_")}";
using (var http = new HttpClient())
{
var webpage = await http.GetStringAsync(url).ConfigureAwait(false);
var matches = Regex.Matches(webpage, "file_url=\"(?<url>.*?)\"");
if (matches.Count == 0)
return null;
var match = matches[rng.Next(0, matches.Count)];
return "http:" + matches[rng.Next(0, matches.Count)].Groups["url"].Value;
}
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task BFO(IUserMessage umsg, [Remainder] string game = null)
{
var channel = (ITextChannel)umsg.Channel;
if (string.IsNullOrWhiteSpace(game))
{
await channel.SendMessageAsync("💢 Please enter a game `(bf3, bf4)`").ConfigureAwait(false);
return;
}
await umsg.Channel.TriggerTypingAsync().ConfigureAwait(false);
using (var http = new HttpClient())
{
http.DefaultRequestHeaders.Clear();
try
{
if (game.Equals("bf3", StringComparison.OrdinalIgnoreCase))
{
var res = await http.GetStringAsync($"http://api.bf3stats.com/global/onlinestats/").ConfigureAwait(false);
var items = JObject.Parse(res);
var sb = new StringBuilder();
var status = items["status"];
var x360 = items["360"];
var ps3 = items["ps3"];
var pc = items["pc"];
var response = $@"```css
[☕ BF3 Status: {status.ToString().ToUpper()}]
XBOX360: [{x360.ToString()}]
PS3: [{ps3.ToString()}]
PC: [{pc.ToString()}]
```";
await channel.SendMessageAsync(response);
}
else if (game.Equals("bf4", StringComparison.OrdinalIgnoreCase))
{
var res = await http.GetStringAsync($"http://api.bf4stats.com/api/onlinePlayers?output=json").ConfigureAwait(false);
var items = JObject.Parse(res);
var sb = new StringBuilder();
var status = !string.IsNullOrEmpty(items.ToString()) ? "OK" : "BAD";
var pc = items["pc"];
var ps3 = items["ps3"];
var ps4 = items["ps4"];
var xbox = items["xbox"];
var xone = items["xone"];
sb.AppendLine("```css");
sb.AppendLine($"[☕ BF4 Status: {status}]");
foreach (var i in items) {
var plat = items[i.Key];
sb.AppendLine($"{plat["label"]}: ✔[{plat["count"]}] / ↑[{plat["peak24"]}]");
}
sb.Append("```");
await channel.SendMessageAsync(sb.ToString());
}
} catch
{
await channel.SendMessageAsync($"💢 BF3/BF4 API is most likely not working at the moment or could not find {game}.").ConfigureAwait(false);
}
}
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task BFU(IUserMessage umsg, string platform, string game, [Remainder] string query = null)
{
var channel = (ITextChannel)umsg.Channel;
if (string.IsNullOrWhiteSpace(platform) || string.IsNullOrWhiteSpace(game) || string.IsNullOrWhiteSpace(query))
{
await channel.SendMessageAsync("💢 Please enter a platform `(pc, xbox, ps3, xone, ps4)`, game `(bf3, bf4)`, followed by a search query.").ConfigureAwait(false);
return;
}
await umsg.Channel.TriggerTypingAsync().ConfigureAwait(false);
using (var http = new HttpClient())
{
http.DefaultRequestHeaders.Clear();
try
{
if (game.Equals("bf3", StringComparison.OrdinalIgnoreCase))
{
var res = await http.GetStringAsync($"http://api.bf3stats.com/{Uri.EscapeUriString(platform)}/playerlist/players={Uri.EscapeUriString(query)}?output=json").ConfigureAwait(false);
var items = JObject.Parse(res);
var sb = new StringBuilder();
var playerName = items["list"][query];
var playerTag = playerName["tag"];
var playerCountryName = playerName["country_name"];
var playerStats = playerName["stats"];
var playerRank = playerStats["rank"];
var playerRank_name = playerRank["name"];
var playerGlobal_Kills = playerStats["global"]["kills"];
var playerGlobal_Deaths = playerStats["global"]["deaths"];
var playerGlobal_KD = Math.Round(Double.Parse(playerGlobal_Kills.ToString()) / Double.Parse(playerGlobal_Deaths.ToString()), 2);
var playerGlobal_Wins = playerStats["global"]["wins"];
var playerGlobal_Losses = playerStats["global"]["losses"];
var playerGlobal_WL = Math.Round(Double.Parse(playerGlobal_Wins.ToString()) / Double.Parse(playerGlobal_Losses.ToString()), 2);
var playerGlobal_Shots = playerStats["global"]["shots"];
var playerGlobal_Hits = playerStats["global"]["hits"];
var playerGlobal_Accuracy = Math.Round(Double.Parse(playerGlobal_Hits.ToString()) / Double.Parse(playerGlobal_Shots.ToString()), 2);
var playerGlobal_ELO = playerStats["global"]["elo"];
var response = $@"```css
[☕ BF3 Player: {query}]
Platform: [{platform.ToUpper()}]
Tag: [{playerTag.ToString()}]
K/D: [{playerGlobal_KD.ToString()}]
W/L: [{playerGlobal_WL.ToString()}]
Accuracy: %[{playerGlobal_Accuracy.ToString()}]
ELO: [{playerGlobal_ELO.ToString()}]
```";
await channel.SendMessageAsync(response);
} else if (game.Equals("bf4", StringComparison.OrdinalIgnoreCase))
{
var res = await http.GetStringAsync($"http://api.bf4stats.com/api/playerInfo?plat={Uri.EscapeUriString(platform)}&name={Uri.EscapeUriString(query)}&output=json").ConfigureAwait(false);
var items = JObject.Parse(res);
var sb = new StringBuilder();
var player = items["player"];
var playerStats = items["stats"];
var playerName = player["name"];
var playerTag = player["tag"];
var playerPlatform = player["plat"];
var playerKills = playerStats["kills"];
var playerDeaths = playerStats["deaths"];
var player_KD = Math.Round(Double.Parse(playerKills.ToString()) / Double.Parse(playerDeaths.ToString()), 2);
var playerWins = playerStats["numWins"];
var playerRounds = playerStats["numRounds"];
var player_WL = Math.Round(Double.Parse(playerWins.ToString()) / Double.Parse(playerRounds.ToString()), 2);
var shotsFired = playerStats["shotsFired"];
var shotsHit = playerStats["shotsHit"];
var accuracy = Math.Round(Double.Parse(shotsHit.ToString()) / Double.Parse(shotsFired.ToString()), 2);
var playerELO = playerStats["elo"];
var response = $@"```css
[☕ BF4 Player: {playerName.ToString()}]
Platform: [{playerPlatform.ToString().ToUpper()}]
Tag: [{playerTag.ToString()}]
K/D: [{player_KD.ToString()}]
W/L: [{player_WL.ToString()}]
Accuracy: %[{accuracy.ToString()}]
ELO: [{playerELO.ToString()}]
```";
await channel.SendMessageAsync(response);
}
}
catch
{
await channel.SendMessageAsync($"💢 BF3/BF4 API is most likely not working at the moment or could not find {query}.").ConfigureAwait(false);
}
}
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -700,7 +683,7 @@ ELO: [{playerELO.ToString()}]
var channel = (ITextChannel)umsg.Channel; var channel = (ITextChannel)umsg.Channel;
if (string.IsNullOrWhiteSpace(target) || string.IsNullOrWhiteSpace(query)) if (string.IsNullOrWhiteSpace(target) || string.IsNullOrWhiteSpace(query))
{ {
await channel.SendMessageAsync("💢 Please enter a target wikia, followed by search query.").ConfigureAwait(false); await channel.SendErrorAsync("Please enter a target wikia, followed by search query.").ConfigureAwait(false);
return; return;
} }
await umsg.Channel.TriggerTypingAsync().ConfigureAwait(false); await umsg.Channel.TriggerTypingAsync().ConfigureAwait(false);
@ -719,7 +702,7 @@ ELO: [{playerELO.ToString()}]
} }
catch catch
{ {
await channel.SendMessageAsync($"💢 Failed finding `{query}`.").ConfigureAwait(false); await channel.SendErrorAsync($"Failed finding `{query}`.").ConfigureAwait(false);
} }
} }
} }
@ -732,7 +715,7 @@ ELO: [{playerELO.ToString()}]
var arg = query; var arg = query;
if (string.IsNullOrWhiteSpace(arg)) if (string.IsNullOrWhiteSpace(arg))
{ {
await channel.SendMessageAsync("💢 Please enter a `ip:port`.").ConfigureAwait(false); await channel.SendErrorAsync("💢 Please enter a `ip:port`.").ConfigureAwait(false);
return; return;
} }
await umsg.Channel.TriggerTypingAsync().ConfigureAwait(false); await umsg.Channel.TriggerTypingAsync().ConfigureAwait(false);
@ -756,7 +739,7 @@ ELO: [{playerELO.ToString()}]
} }
catch catch
{ {
await channel.SendMessageAsync($"💢 Failed finding `{arg}`.").ConfigureAwait(false); await channel.SendErrorAsync($"Failed finding `{arg}`.").ConfigureAwait(false);
} }
} }
} }
@ -769,7 +752,7 @@ ELO: [{playerELO.ToString()}]
var arg = query; var arg = query;
if (string.IsNullOrWhiteSpace(arg)) if (string.IsNullOrWhiteSpace(arg))
{ {
await channel.SendMessageAsync("💢 Please enter a `ip:port`.").ConfigureAwait(false); await channel.SendErrorAsync("Please enter `ip:port`.").ConfigureAwait(false);
return; return;
} }
await umsg.Channel.TriggerTypingAsync().ConfigureAwait(false); await umsg.Channel.TriggerTypingAsync().ConfigureAwait(false);
@ -796,15 +779,86 @@ ELO: [{playerELO.ToString()}]
} }
catch catch
{ {
await channel.SendMessageAsync($"💢 Failed finding server `{arg}`.").ConfigureAwait(false); await channel.SendErrorAsync($"Failed finding server `{arg}`.").ConfigureAwait(false);
} }
} }
} }
public enum DapiSearchType
{
Safebooru,
Gelbooru,
Konachan,
Rule34,
Yandere
}
public static async Task InternalDapiCommand(IUserMessage umsg, string tag, DapiSearchType type)
{
var channel = (ITextChannel)umsg.Channel;
tag = tag?.Trim() ?? "";
var url = await InternalDapiSearch(tag, type).ConfigureAwait(false);
if (url == null)
await channel.SendErrorAsync(umsg.Author.Mention + " No results.");
else
await channel.EmbedAsync(new EmbedBuilder().WithOkColor()
.WithDescription(umsg.Author.Mention + " " + tag)
.WithImageUrl(url)
.WithFooter(efb => efb.WithText(type.ToString()))
.Build()).ConfigureAwait(false);
}
public static async Task<string> InternalDapiSearch(string tag, DapiSearchType type)
{
tag = tag?.Replace(" ", "_");
string website = "";
switch (type)
{
case DapiSearchType.Safebooru:
website = $"https://safebooru.org/index.php?page=dapi&s=post&q=index&limit=100&tags={tag}";
break;
case DapiSearchType.Gelbooru:
website = $"http://gelbooru.com/index.php?page=dapi&s=post&q=index&limit=100&tags={tag}";
break;
case DapiSearchType.Rule34:
website = $"https://rule34.xxx/index.php?page=dapi&s=post&q=index&limit=100&tags={tag}";
break;
case DapiSearchType.Konachan:
website = $"https://konachan.com/post.xml?s=post&q=index&limit=100&tags={tag}";
break;
case DapiSearchType.Yandere:
website = $"https://yande.re/post.xml?limit=100&tags={tag}";
break;
}
try
{
using (var http = new HttpClient())
{
http.AddFakeHeaders();
var data = await http.GetStreamAsync(website);
var doc = new XmlDocument();
doc.Load(data);
var node = doc.LastChild.ChildNodes[new NadekoRandom().Next(0, doc.LastChild.ChildNodes.Count)];
var url = node.Attributes["file_url"].Value;
if (!url.StartsWith("http"))
url = "https:" + url;
return url;
}
}
catch
{
return null;
}
}
public static async Task<bool> ValidateQuery(ITextChannel ch, string query) public static async Task<bool> ValidateQuery(ITextChannel ch, string query)
{ {
if (!string.IsNullOrEmpty(query.Trim())) return true; if (!string.IsNullOrEmpty(query.Trim())) return true;
await ch.SendMessageAsync("Please specify search parameters.").ConfigureAwait(false); await ch.SendErrorAsync("Please specify search parameters.").ConfigureAwait(false);
return false; return false;
} }
} }

View File

@ -3,6 +3,8 @@ using Discord.Commands;
using NadekoBot.Attributes; using NadekoBot.Attributes;
using NadekoBot.Extensions; using NadekoBot.Extensions;
using System; using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text; using System.Text;
@ -10,24 +12,19 @@ using System.Threading.Tasks;
namespace NadekoBot.Modules.Utility namespace NadekoBot.Modules.Utility
{ {
[Group]
public partial class Utility public partial class Utility
{ {
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public static async Task Calculate(IUserMessage msg, [Remainder] string expression) public static async Task Calculate(IUserMessage msg, [Remainder] string expression)
{
try
{ {
var expr = new NCalc.Expression(expression, NCalc.EvaluateOptions.IgnoreCase); var expr = new NCalc.Expression(expression, NCalc.EvaluateOptions.IgnoreCase);
expr.EvaluateParameter += Expr_EvaluateParameter; expr.EvaluateParameter += Expr_EvaluateParameter;
var result = expr.Evaluate(); var result = expr.Evaluate();
await msg.Reply(string.Format("⚙ `{0}`", expr.Error ?? result)); if (expr.Error == null)
} await msg.Channel.SendConfirmAsync("Result", $"{result}");
catch (Exception e) else
{ await msg.Channel.SendErrorAsync($"⚙ Error", expr.Error);
await msg.Reply($"Failed to evaluate: {e.Message} ");
}
} }
private static void Expr_EvaluateParameter(string name, NCalc.ParameterArgs args) private static void Expr_EvaluateParameter(string name, NCalc.ParameterArgs args)
@ -44,20 +41,25 @@ namespace NadekoBot.Modules.Utility
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task CalcOps(IUserMessage msg) public async Task CalcOps(IUserMessage msg)
{ {
StringBuilder builder = new StringBuilder(); var selection = typeof(Math).GetTypeInfo().GetMethods().Except(typeof(object).GetTypeInfo().GetMethods()).Distinct(new MethodInfoEqualityComparer()).Select(x =>
var selection = typeof(Math).GetTypeInfo().GetMethods().Except(typeof(object).GetTypeInfo().GetMethods()).Select(x =>
{ {
var name = x.Name; return x.Name;
if (x.GetParameters().Any()) })
.Except(new[] { "ToString",
"Equals",
"GetHashCode",
"GetType"});
await msg.Channel.SendConfirmAsync("Available functions in calc", string.Join(", ", selection));
}
}
class MethodInfoEqualityComparer : IEqualityComparer<MethodInfo>
{ {
name += " (" + string.Join(", ", x.GetParameters().Select(y => y.IsOptional ? $"[{y.ParameterType.Name + " " + y.Name }]" : y.ParameterType.Name + " " + y.Name)) + ")"; public bool Equals(MethodInfo x, MethodInfo y) => x.Name == y.Name;
}
return name; public int GetHashCode(MethodInfo obj) => obj.Name.GetHashCode();
});
foreach (var method in selection) builder.AppendLine(method);
await msg.ReplyLong(builder.ToString());
}
} }
class ExpressionContext class ExpressionContext
{ {
public double Pi { get; set; } = Math.PI; public double Pi { get; set; } = Math.PI;

View File

@ -10,58 +10,46 @@ using System.Threading.Tasks;
namespace NadekoBot.Modules.Utility namespace NadekoBot.Modules.Utility
{ {
partial class Utility : DiscordModule public partial class Utility
{ {
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task TogetherTube(IUserMessage imsg) public async Task ServerInfo(IUserMessage msg, string guildName = null)
{
var channel = (ITextChannel)imsg.Channel;
Uri target;
using (var http = new HttpClient())
{
var res = await http.GetAsync("https://togethertube.com/room/create").ConfigureAwait(false);
target = res.RequestMessage.RequestUri;
}
await channel.SendMessageAsync($"🎞 {imsg.Author.Mention}, **Your new video room created. Join and invite to watch videos together with friends:** {target}")
.ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task ServerInfo(IUserMessage msg, string guild = null)
{ {
var channel = (ITextChannel)msg.Channel; var channel = (ITextChannel)msg.Channel;
guild = guild?.ToUpperInvariant(); guildName = guildName?.ToUpperInvariant();
IGuild server; IGuild guild;
if (guild == null) if (string.IsNullOrWhiteSpace(guildName))
server = channel.Guild; guild = channel.Guild;
else else
server = _client.GetGuilds().Where(g => g.Name.ToUpperInvariant() == guild.ToUpperInvariant()).FirstOrDefault(); guild = NadekoBot.Client.GetGuilds().Where(g => g.Name.ToUpperInvariant() == guildName.ToUpperInvariant()).FirstOrDefault();
if (server == null) if (guild == null)
return; return;
var ownername = await guild.GetUserAsync(guild.OwnerId);
var textchn = (await guild.GetTextChannelsAsync()).Count();
var voicechn = (await guild.GetVoiceChannelsAsync()).Count();
var createdAt = new DateTime(2015, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(server.Id >> 22); var createdAt = new DateTime(2015, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(guild.Id >> 22);
var sb = new StringBuilder(); var sb = new StringBuilder();
var users = await server.GetUsersAsync(); var users = await guild.GetUsersAsync().ConfigureAwait(false);
sb.AppendLine($@"__`Name:`__ **{server.Name}** var embed = new EmbedBuilder()
__`Owner:`__ **{await server.GetUserAsync(server.OwnerId)}** .WithAuthor(eab => eab.WithName("Server Info"))
__`ID:`__ **{server.Id}** .WithTitle(guild.Name)
__`Icon URL:`__ { server.IconUrl} .AddField(fb => fb.WithName("**ID**").WithValue(guild.Id.ToString()).WithIsInline(true))
__`TextChannels:`__ **{(await server.GetTextChannelsAsync()).Count()}** `VoiceChannels:` **{(await server.GetVoiceChannelsAsync()).Count()}** .AddField(fb => fb.WithName("**Owner**").WithValue(ownername.ToString()).WithIsInline(true))
__`Members:`__ **{users.Count}** `-` {users.Count(u => u.Status == UserStatus.Online)}💚 {users.Count(u => u.Status == UserStatus.Idle)}🔶 {users.Count(u => u.Status == UserStatus.DoNotDisturb)}🔴 {users.Count(u=> u.Status == UserStatus.Offline || u.Status == UserStatus.Unknown)} .AddField(fb => fb.WithName("**Members**").WithValue(users.Count.ToString()).WithIsInline(true))
__`Roles:`__ **{server.Roles.Count()}** .AddField(fb => fb.WithName("**Text Channels**").WithValue(textchn.ToString()).WithIsInline(true))
__`Created At:`__ **{createdAt.ToString("dd.MM.yyyy HH:mm")}** .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))
if (server.Emojis.Count() > 0) .AddField(fb => fb.WithName("**Region**").WithValue(guild.VoiceRegionId.ToString()).WithIsInline(true))
sb.AppendLine($"__`Custom Emojis:`__ *{string.Join(", ", server.Emojis)}*"); .AddField(fb => fb.WithName("**Roles**").WithValue(guild.Roles.Count().ToString()).WithIsInline(true))
if (server.Features.Count() > 0) .WithImage(tn => tn.WithUrl(guild.IconUrl))
sb.AppendLine($"__`Features:`__ **{string.Join(", ", server.Features)}**"); .WithOkColor();
if (!string.IsNullOrWhiteSpace(server.SplashUrl)) if (guild.Emojis.Count() > 0)
sb.AppendLine($"__`Region:`__ **{server.VoiceRegionId}**"); {
await msg.Reply(sb.ToString()).ConfigureAwait(false); embed.AddField(fb => fb.WithName("**Custom Emojis**").WithValue(Format.Italics(string.Join(", ", guild.Emojis))).WithIsInline(true));
}
await msg.Channel.EmbedAsync(embed.Build()).ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -72,12 +60,15 @@ __`Created At:`__ **{createdAt.ToString("dd.MM.yyyy HH:mm")}**
if (ch == null) if (ch == null)
return; return;
var createdAt = new DateTime(2015, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(ch.Id >> 22); var createdAt = new DateTime(2015, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(ch.Id >> 22);
var toReturn = $@"__`Name:`__ **#{ch.Name}** var usercount = (await ch.GetUsersAsync()).Count();
__`ID:`__ **{ch.Id}** var embed = new EmbedBuilder()
__`Created At:`__ **{createdAt.ToString("dd.MM.yyyy HH:mm")}** .WithTitle(ch.Name)
__`Topic:`__ {ch.Topic} .WithDescription(ch.Topic?.SanitizeMentions())
__`Users:`__ **{(await ch.GetUsersAsync()).Count()}**"; .AddField(fb => fb.WithName("**ID**").WithValue(ch.Id.ToString()).WithIsInline(true))
await msg.Reply(toReturn).ConfigureAwait(false); .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))
.WithOkColor();
await msg.Channel.EmbedAsync(embed.Build()).ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -86,20 +77,49 @@ __`Users:`__ **{(await ch.GetUsersAsync()).Count()}**";
{ {
var channel = (ITextChannel)msg.Channel; var channel = (ITextChannel)msg.Channel;
var user = usr ?? msg.Author as IGuildUser; var user = usr ?? msg.Author as IGuildUser;
if (user == null) if (user == null)
return; return;
var toReturn = $"👤 __`Name:`__ **{user.Username}#{user.Discriminator}**\n";
if (!string.IsNullOrWhiteSpace(user.Nickname)) var embed = new EmbedBuilder()
toReturn += $"🆕 __`Nickname:`__ **{user.Nickname}** "; .AddField(fb => fb.WithName("**Name**").WithValue($"**{user.Username}**#{user.Discriminator}").WithIsInline(true));
toReturn += $@"🏷 __`ID:`__ **{user.Id}** if (!string.IsNullOrWhiteSpace(user.Nickname)) {
🎮 __`Current Game:`__ **{(user.Game?.Name == null ? "-" : user.Game.Name)}** embed.AddField(fb => fb.WithName("**Nickname**").WithValue(user.Nickname).WithIsInline(true));
📅 __`Joined Server:`__ **{user.JoinedAt?.ToString("dd.MM.yyyy HH:mm")}** }
🗓 __`Joined Discord:`__ **{user.CreatedAt.ToString("dd.MM.yyyy HH:mm")}** embed.AddField(fb => fb.WithName("**ID**").WithValue(user.Id.ToString()).WithIsInline(true))
__`Roles:`__ **({user.Roles.Count()}) - {string.Join(", ", user.Roles.Select(r => r.Name)).SanitizeMentions()}**"; .AddField(fb => fb.WithName("**Joined Server**").WithValue($"{user.JoinedAt?.ToString("dd.MM.yyyy HH:mm")}").WithIsInline(true))
if (!string.IsNullOrWhiteSpace(user.AvatarUrl)) .AddField(fb => fb.WithName("**Joined Discord**").WithValue($"{user.CreatedAt.ToString("dd.MM.yyyy HH:mm")}").WithIsInline(true))
toReturn += $@" .AddField(fb => fb.WithName("**Current Game**").WithValue($"{(user.Game?.Name == null ? "-" : user.Game.Name)}").WithIsInline(true))
📷 __`Avatar URL:`__ **{await NadekoBot.Google.ShortenUrl(user.AvatarUrl).ConfigureAwait(false)}**"; .AddField(fb => fb.WithName("**Roles**").WithValue($"**({user.Roles.Count()})** - {string.Join(", ", user.Roles.Select(r => r.Name)).SanitizeMentions()}").WithIsInline(true))
await msg.Reply(toReturn).ConfigureAwait(false); .WithThumbnail(tn => tn.WithUrl(user.AvatarUrl))
.WithOkColor();
await msg.Channel.EmbedAsync(embed.Build()).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[OwnerOnly]
public async Task Activity(IUserMessage imsg, int page = 1)
{
const int activityPerPage = 15;
page -= 1;
if (page < 0)
return;
int startCount = page * activityPerPage;
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");
}
await imsg.Channel.EmbedAsync(new EmbedBuilder().WithTitle($"Activity Page #{page}")
.WithOkColor()
.WithFooter(efb => efb.WithText($"{NadekoBot.CommandHandler.UserMessagesSent.Count} users total."))
.WithDescription(str.ToString())
.Build());
} }
} }
} }

View File

@ -27,14 +27,14 @@ namespace NadekoBot.Modules.Utility
IEnumerable<Quote> quotes; IEnumerable<Quote> quotes;
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
quotes = uow.Quotes.GetGroup(page * 16, 16); quotes = uow.Quotes.GetGroup(channel.Guild.Id, page * 16, 16);
} }
if (quotes.Any()) if (quotes.Any())
await channel.SendMessageAsync($"💬 **Page {page + 1} of quotes:**\n```xl\n" + String.Join("\n", quotes.Select((q) => $"{q.Keyword,-20} by {q.AuthorName}")) + "\n```") await channel.SendConfirmAsync($"💬 **Page {page + 1} of quotes:**\n```xl\n" + String.Join("\n", quotes.Select((q) => $"{q.Keyword,-20} by {q.AuthorName}")) + "\n```")
.ConfigureAwait(false); .ConfigureAwait(false);
else else
await channel.SendMessageAsync(" **No quotes on this page.**").ConfigureAwait(false); await channel.SendErrorAsync("No quotes on this page.").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -83,7 +83,7 @@ namespace NadekoBot.Modules.Utility
}); });
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
} }
await channel.SendMessageAsync("✅ **Quote added.**").ConfigureAwait(false); await channel.SendConfirmAsync("✅ Quote added.").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -105,7 +105,7 @@ namespace NadekoBot.Modules.Utility
if (qs==null || !qs.Any()) if (qs==null || !qs.Any())
{ {
response = " **No quotes found.**"; await channel.SendErrorAsync("No quotes found.");
return; return;
} }
@ -115,7 +115,7 @@ namespace NadekoBot.Modules.Utility
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
response = "🗑 **Deleted a random quote.**"; response = "🗑 **Deleted a random quote.**";
} }
await channel.SendMessageAsync(response); await channel.SendConfirmAsync(response);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -139,7 +139,7 @@ namespace NadekoBot.Modules.Utility
await uow.CompleteAsync(); await uow.CompleteAsync();
} }
await channel.SendMessageAsync($"🗑 **Deleted all quotes** with **{keyword}** keyword."); await channel.SendConfirmAsync($"🗑 **Deleted all quotes** with **{keyword}** keyword.");
} }
} }
} }

View File

@ -124,7 +124,7 @@ namespace NadekoBot.Modules.Utility
if (ch == null) if (ch == null)
{ {
await channel.SendMessageAsync($"⚠️ {umsg.Author.Mention} Something went wrong (channel cannot be found) ;(").ConfigureAwait(false); await channel.SendErrorAsync($"{umsg.Author.Mention} Something went wrong (channel cannot be found) ;(").ConfigureAwait(false);
return; return;
} }
@ -132,7 +132,7 @@ namespace NadekoBot.Modules.Utility
if (m.Length == 0) if (m.Length == 0)
{ {
await channel.SendMessageAsync("❎ **Not a valid time format.** type `-h .remind`").ConfigureAwait(false); await channel.SendErrorAsync("Not a valid time format. Type `-h .remind`").ConfigureAwait(false);
return; return;
} }
@ -157,7 +157,7 @@ namespace NadekoBot.Modules.Utility
(groupName == "hours" && value > 23) || (groupName == "hours" && value > 23) ||
(groupName == "minutes" && value > 59)) (groupName == "minutes" && value > 59))
{ {
await channel.SendMessageAsync($"⚠️ Invalid {groupName} value.").ConfigureAwait(false); await channel.SendErrorAsync($"Invalid {groupName} value.").ConfigureAwait(false);
return; return;
} }
else else
@ -187,7 +187,7 @@ namespace NadekoBot.Modules.Utility
await uow.CompleteAsync(); await uow.CompleteAsync();
} }
try { await channel.SendMessageAsync($"⏰ I will remind **\"{(ch is ITextChannel ? ((ITextChannel)ch).Name : umsg.Author.Username)}\"** to **\"{message.SanitizeMentions()}\"** in **{output}** `({time:d.M.yyyy.} at {time:HH:mm})`").ConfigureAwait(false); } catch { } try { await channel.SendConfirmAsync($"⏰ I will remind **\"{(ch is ITextChannel ? ((ITextChannel)ch).Name : umsg.Author.Username)}\"** to **\"{message.SanitizeMentions()}\"** in **{output}** `({time:d.M.yyyy.} at {time:HH:mm})`").ConfigureAwait(false); } catch { }
await StartReminder(rem); await StartReminder(rem);
} }
@ -206,7 +206,7 @@ namespace NadekoBot.Modules.Utility
uow.BotConfig.GetOrCreate().RemindMessageFormat = arg.Trim(); uow.BotConfig.GetOrCreate().RemindMessageFormat = arg.Trim();
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
} }
await channel.SendMessageAsync("🆗 New remind template set."); await channel.SendConfirmAsync("🆗 New remind template set.");
} }
} }
} }

View File

@ -25,7 +25,7 @@ namespace NadekoBot.Modules.Utility
{ {
public static List<ConvertUnit> Units { get; set; } = new List<ConvertUnit>(); public static List<ConvertUnit> Units { get; set; } = new List<ConvertUnit>();
private static Logger _log; private static Logger _log { get; }
private static Timer _timer; private static Timer _timer;
private static TimeSpan updateInterval = new TimeSpan(12, 0, 0); private static TimeSpan updateInterval = new TimeSpan(12, 0, 0);
@ -104,15 +104,14 @@ namespace NadekoBot.Modules.Utility
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task ConvertList(IUserMessage msg) public async Task ConvertList(IUserMessage msg)
{ {
var sb = new StringBuilder("Units that can be used by the converter: \n"); var res = Units.GroupBy(x => x.UnitType)
var res = Units.GroupBy(x => x.UnitType); .Aggregate(new EmbedBuilder().WithTitle("__Units which can be used by the converter__")
foreach (var group in res) .WithOkColor(),
{ (embed, g) => embed.AddField(efb =>
sb.AppendLine($"{group.Key}: ```xl"); efb.WithName(g.Key.ToTitleCase())
sb.AppendLine(string.Join(",", group.Select(x => x.Triggers.FirstOrDefault()).OrderBy(x => x))); .WithValue(String.Join(", ", g.Select(x => x.Triggers.FirstOrDefault())
sb.AppendLine("```"); .OrderBy(x => x)))));
} await msg.Channel.EmbedAsync(res.Build());
await msg.ReplyLong(sb.ToString(), breakOn: new[] { "```xl\n", "\n" });
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
public async Task Convert(IUserMessage msg, string origin, string target, decimal value) public async Task Convert(IUserMessage msg, string origin, string target, decimal value)
@ -121,12 +120,12 @@ namespace NadekoBot.Modules.Utility
var targetUnit = Units.Find(x => x.Triggers.Select(y => y.ToLowerInvariant()).Contains(target.ToLowerInvariant())); var targetUnit = Units.Find(x => x.Triggers.Select(y => y.ToLowerInvariant()).Contains(target.ToLowerInvariant()));
if (originUnit == null || targetUnit == null) if (originUnit == null || targetUnit == null)
{ {
await msg.Reply(string.Format("Cannot convert {0} to {1}: units not found", origin, target)); await msg.Channel.SendErrorAsync(string.Format("Cannot convert {0} to {1}: units not found", origin, target));
return; return;
} }
if (originUnit.UnitType != targetUnit.UnitType) if (originUnit.UnitType != targetUnit.UnitType)
{ {
await msg.Reply(string.Format("Cannot convert {0} to {1}: types of unit are not equal", originUnit.Triggers.First(), targetUnit.Triggers.First())); await msg.Channel.SendErrorAsync(string.Format("Cannot convert {0} to {1}: types of unit are not equal", originUnit.Triggers.First(), targetUnit.Triggers.First()));
return; return;
} }
decimal res; decimal res;
@ -170,7 +169,7 @@ namespace NadekoBot.Modules.Utility
} }
res = Math.Round(res, 4); res = Math.Round(res, 4);
await msg.Reply(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 msg.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)));
} }
} }

View File

@ -15,6 +15,7 @@ using Discord.API;
using Embed = Discord.API.Embed; using Embed = Discord.API.Embed;
using EmbedAuthor = Discord.API.EmbedAuthor; using EmbedAuthor = Discord.API.EmbedAuthor;
using EmbedField = Discord.API.EmbedField; using EmbedField = Discord.API.EmbedField;
using System.Net.Http;
namespace NadekoBot.Modules.Utility namespace NadekoBot.Modules.Utility
{ {
@ -22,11 +23,32 @@ namespace NadekoBot.Modules.Utility
[NadekoModule("Utility", ".")] [NadekoModule("Utility", ".")]
public partial class Utility : DiscordModule public partial class Utility : DiscordModule
{ {
public Utility(ILocalization loc, CommandService cmds, ShardedDiscordClient client) : base(loc, cmds, client) public Utility() : base()
{ {
} }
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task TogetherTube(IUserMessage imsg)
{
var channel = (ITextChannel)imsg.Channel;
Uri target;
using (var http = new HttpClient())
{
var res = await http.GetAsync("https://togethertube.com/room/create").ConfigureAwait(false);
target = res.RequestMessage.RequestUri;
}
await channel.EmbedAsync(new EmbedBuilder().WithOkColor()
.WithAuthor(eab => eab.WithIconUrl("https://togethertube.com/assets/img/favicons/favicon-32x32.png")
.WithName("Together Tube")
.WithUrl("https://togethertube.com/"))
.WithDescription($"{imsg.Author.Mention} Here is your room link:\n{target}")
.Build());
}
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task WhosPlaying(IUserMessage umsg, [Remainder] string game = null) public async Task WhosPlaying(IUserMessage umsg, [Remainder] string game = null)
@ -35,16 +57,18 @@ namespace NadekoBot.Modules.Utility
game = game.Trim().ToUpperInvariant(); game = game.Trim().ToUpperInvariant();
if (string.IsNullOrWhiteSpace(game)) if (string.IsNullOrWhiteSpace(game))
return; return;
var arr = (await (umsg.Channel as IGuildChannel).Guild.GetUsersAsync()) var usrs = (await (umsg.Channel as IGuildChannel).Guild.GetUsersAsync())
.Where(u => u.Game?.Name?.ToUpperInvariant() == game) .Where(u => u.Game?.Name?.ToUpperInvariant() == game)
.Select(u => u.Username) .Select(u => u.Username)
.ToList(); .ToList();
int i = 0; int i = 0;
if (!arr.Any()) if (!usrs.Any())
await channel.SendErrorAsync("Nobody is playing that game.").ConfigureAwait(false); await channel.SendErrorAsync("Nobody is playing that game.").ConfigureAwait(false);
else else
await channel.SendMessageAsync("```css\n" + string.Join("\n", arr.GroupBy(item => (i++) / 3).Select(ig => string.Concat(ig.Select(el => $"• {el,-35}")))) + "\n```").ConfigureAwait(false); await channel.SendConfirmAsync($"List of users playing {game} game. Total {usrs.Count}.", "```css\n" + string.Join("\n", usrs.Take(30).GroupBy(item => (i++) / 2)
.Select(ig => string.Concat(ig.Select(el => $"• {el,-27}")))) + "\n```")
.ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -55,7 +79,7 @@ namespace NadekoBot.Modules.Utility
return; return;
var channel = (ITextChannel)umsg.Channel; var channel = (ITextChannel)umsg.Channel;
var arg = roles.Split(',').Select(r => r.Trim().ToUpperInvariant()); var arg = roles.Split(',').Select(r => r.Trim().ToUpperInvariant());
string send = _l[" **Here is a list of users in a specfic role:**"]; 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")) foreach (var roleStr in arg.Where(str => !string.IsNullOrWhiteSpace(str) && str != "@EVERYONE" && str != "EVERYONE"))
{ {
var role = channel.Guild.Roles.Where(r => r.Name.ToUpperInvariant() == roleStr).FirstOrDefault(); var role = channel.Guild.Roles.Where(r => r.Name.ToUpperInvariant() == roleStr).FirstOrDefault();
@ -69,16 +93,16 @@ namespace NadekoBot.Modules.Utility
{ {
if (!usr.GetPermissions(channel).ManageMessages) if (!usr.GetPermissions(channel).ManageMessages)
{ {
await channel.SendMessageAsync($"⚠️ {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 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);
return; return;
} }
var curstr = send.Substring(0, 2000); var curstr = send.Substring(0, 2000);
await channel.SendMessageAsync(curstr.Substring(0, await channel.SendConfirmAsync(curstr.Substring(0,
curstr.LastIndexOf(", ", StringComparison.Ordinal) + 1)).ConfigureAwait(false); curstr.LastIndexOf(", ", StringComparison.Ordinal) + 1)).ConfigureAwait(false);
send = curstr.Substring(curstr.LastIndexOf(", ", StringComparison.Ordinal) + 1) + send = curstr.Substring(curstr.LastIndexOf(", ", StringComparison.Ordinal) + 1) +
send.Substring(2000); send.Substring(2000);
} }
await channel.SendMessageAsync(send).ConfigureAwait(false); await channel.SendConfirmAsync(send).ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -95,7 +119,7 @@ namespace NadekoBot.Modules.Utility
} }
builder.Append("```"); builder.Append("```");
await msg.Reply(builder.ToString()); await msg.Channel.SendConfirmAsync(builder.ToString());
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -103,20 +127,20 @@ namespace NadekoBot.Modules.Utility
public async Task UserId(IUserMessage msg, IGuildUser target = null) public async Task UserId(IUserMessage msg, IGuildUser target = null)
{ {
var usr = target ?? msg.Author; var usr = target ?? msg.Author;
await msg.Reply($"🆔 of the user **{ usr.Username }** is `{ usr.Id }`").ConfigureAwait(false); await msg.Channel.SendConfirmAsync($"🆔 of the user **{ usr.Username }** is `{ usr.Id }`").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
public async Task ChannelId(IUserMessage msg) public async Task ChannelId(IUserMessage msg)
{ {
await msg.Reply($" This **Channel's ID** is `{msg.Channel.Id}`").ConfigureAwait(false); await msg.Channel.SendConfirmAsync($"🆔 of this channel is `{msg.Channel.Id}`").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task ServerId(IUserMessage msg) public async Task ServerId(IUserMessage msg)
{ {
await msg.Reply($" This **Server's ID** is `{((ITextChannel)msg.Channel).Guild.Id}`").ConfigureAwait(false); await msg.Channel.SendConfirmAsync($"🆔 of this server is `{((ITextChannel)msg.Channel).Guild.Id}`").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -130,13 +154,30 @@ namespace NadekoBot.Modules.Utility
if (page < 1 || page > 100) if (page < 1 || page > 100)
return; return;
if (target != null) if (target != null)
{ {
await msg.Reply($"⚔ **Page #{page} of roles for {target.Username}:** ```css\n• " + string.Join("\n• ", target.Roles.Except(new[] { guild.EveryoneRole }).OrderBy(r => r.Position).Skip((page - 1) * RolesPerPage).Take(RolesPerPage)).SanitizeMentions() + "\n```"); var roles = target.Roles.Except(new[] { guild.EveryoneRole }).OrderBy(r => -r.Position).Skip((page - 1) * RolesPerPage).Take(RolesPerPage);
if (!roles.Any())
{
await channel.SendErrorAsync("No roles on this page.").ConfigureAwait(false);
} }
else else
{ {
await msg.Reply($"⚔ **Page #{page} of all roles on this server:** ```css\n• " + string.Join("\n• ", guild.Roles.Except(new[] { guild.EveryoneRole }).OrderBy(r => r.Position).Skip((page - 1) * RolesPerPage).Take(RolesPerPage)).SanitizeMentions() + "\n```"); await channel.SendConfirmAsync($"⚔ **Page #{page} of roles for {target.Username}**", $"```css\n• " + string.Join("\n• ", roles).SanitizeMentions() + "\n```").ConfigureAwait(false);
}
}
else
{
var roles = guild.Roles.Except(new[] { guild.EveryoneRole }).OrderBy(r => -r.Position).Skip((page - 1) * RolesPerPage).Take(RolesPerPage);
if (!roles.Any())
{
await channel.SendErrorAsync("No roles on this 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);
}
} }
} }
@ -153,9 +194,9 @@ namespace NadekoBot.Modules.Utility
var topic = channel.Topic; var topic = channel.Topic;
if (string.IsNullOrWhiteSpace(topic)) if (string.IsNullOrWhiteSpace(topic))
await channel.SendMessageAsync("❎ **No topic set.**"); await channel.SendErrorAsync("No topic set.");
else else
await channel.SendMessageAsync(" **Topic:** " + topic); await channel.SendConfirmAsync("Channel topic", topic);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -166,64 +207,23 @@ namespace NadekoBot.Modules.Utility
var stats = NadekoBot.Stats; var stats = NadekoBot.Stats;
await channel.EmbedAsync( await channel.EmbedAsync(
new Embed() new EmbedBuilder().WithOkColor()
{ .WithAuthor(eab => eab.WithName($"NadekoBot v{StatsService.BotVersion}")
Author = new EmbedAuthor() .WithUrl("http://nadekobot.readthedocs.io/en/latest/")
{ .WithIconUrl("https://cdn.discordapp.com/avatars/116275390695079945/b21045e778ef21c96d175400e779f0fb.jpg"))
Name = $"NadekoBot v{StatsService.BotVersion}", .AddField(efb => efb.WithName(Format.Bold("Author")).WithValue(stats.Author).WithIsInline(true))
Url = "http://nadekobot.readthedocs.io/en/latest/", .AddField(efb => efb.WithName(Format.Bold("Library")).WithValue(stats.Library).WithIsInline(true))
IconUrl = "https://cdn.discordapp.com/avatars/116275390695079945/b21045e778ef21c96d175400e779f0fb.jpg" .AddField(efb => efb.WithName(Format.Bold("Bot ID")).WithValue(NadekoBot.Client.GetCurrentUser().Id.ToString()).WithIsInline(true))
}, .AddField(efb => efb.WithName(Format.Bold("Commands Ran")).WithValue(stats.CommandsRan.ToString()).WithIsInline(true))
Fields = new[] { .AddField(efb => efb.WithName(Format.Bold("Messages")).WithValue($"{stats.MessageCounter} ({stats.MessagesPerSecond:F2}/sec)").WithIsInline(true))
new EmbedField() { .AddField(efb => efb.WithName(Format.Bold("Memory")).WithValue($"{stats.Heap} MB").WithIsInline(true))
Name = "Author", .AddField(efb => efb.WithName(Format.Bold("Owner ID(s)")).WithValue(stats.OwnerIds).WithIsInline(true))
Value = stats.Author, .AddField(efb => efb.WithName(Format.Bold("Uptime")).WithValue(stats.GetUptimeString("\n")).WithIsInline(true))
Inline = true .AddField(efb => efb.WithName(Format.Bold("Presence")).WithValue($"{NadekoBot.Client.GetGuilds().Count} Servers\n{stats.TextChannels} Text Channels\n{stats.VoiceChannels} Voice Channels").WithIsInline(true))
}, #if !GLOBAL_NADEKO
new EmbedField() { .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."))
Name = "Library", #endif
Value = stats.Library, .Build());
Inline = true
},
new EmbedField() {
Name = "Bot ID",
Value = NadekoBot.Client.GetCurrentUser().Id.ToString(),
Inline = true
},
new EmbedField() {
Name = "Commands Ran",
Value = stats.CommandsRan.ToString(),
Inline = true
},
new EmbedField() {
Name = "Messages",
Value = $"{stats.MessageCounter} [{stats.MessagesPerSecond:F2}/sec]",
Inline = true
},
new EmbedField() {
Name = "Memory",
Value = $"{stats.Heap} MB",
Inline = true
},
new EmbedField() {
Name = "Owner ID(s)",
Value = stats.OwnerIds,
Inline = true
},
new EmbedField() {
Name = "Uptime",
Value = stats.GetUptimeString("\n"),
Inline = true
},
new EmbedField() {
Name = "Presence",
Value = $"{NadekoBot.Client.GetGuilds().Count} servers\n{stats.TextChannels} Text Channels\n{stats.VoiceChannels} Voice Channels",
Inline = true
},
},
Color = NadekoBot.OkColor
});
} }
private Regex emojiFinder { get; } = new Regex(@"<:(?<name>.+?):(?<id>\d*)>", RegexOptions.Compiled); private Regex emojiFinder { get; } = new Regex(@"<:(?<name>.+?):(?<id>\d*)>", RegexOptions.Compiled);
@ -235,6 +235,9 @@ namespace NadekoBot.Modules.Utility
var result = string.Join("\n", matches.Cast<Match>() var result = string.Join("\n", matches.Cast<Match>()
.Select(m => $"**Name:** {m.Groups["name"]} **Link:** http://discordapp.com/api/emojis/{m.Groups["id"]}.png")); .Select(m => $"**Name:** {m.Groups["name"]} **Link:** http://discordapp.com/api/emojis/{m.Groups["id"]}.png"));
if (string.IsNullOrWhiteSpace(result))
await msg.Channel.SendErrorAsync("No special emojis found.");
else
await msg.Channel.SendMessageAsync(result).ConfigureAwait(false); await msg.Channel.SendMessageAsync(result).ConfigureAwait(false);
} }
@ -254,43 +257,16 @@ namespace NadekoBot.Modules.Utility
if (!guilds.Any()) if (!guilds.Any())
{ {
await channel.SendMessageAsync("❎ No servers found on that page.").ConfigureAwait(false); await channel.SendErrorAsync("No servers found on that page.").ConfigureAwait(false);
return; return;
} }
await channel.SendMessageAsync(String.Join("\n", guilds.Select(g => $"```css\nName: {g.Name} ID:{g.Id} Members:#{g.GetUsers().Count} OwnerID: {g.OwnerId} ```"))).ConfigureAwait(false); await channel.EmbedAsync(guilds.Aggregate(new EmbedBuilder().WithOkColor(),
} (embed, g) => embed.AddField(efb => efb.WithName(g.Name)
.WithValue($"```css\nID: {g.Id}\nMembers: {g.GetUsers().Count}\nOwnerID: {g.OwnerId} ```")
//[NadekoCommand, Usage, Description, Aliases] .WithIsInline(false)))
//[RequireContext(ContextType.Guild)] .Build())
//public async Task TextToImage(IUserMessage msg, [Remainder] string arg) .ConfigureAwait(false);
//{ }
// var channel = (ITextChannel)msg.Channel;
// const string bgName = "xbiy3";
// if (string.IsNullOrWhiteSpace(arg))
// return;
// using (var http = new HttpClient())
// {
// http.AddFakeHeaders();
// http.DefaultRequestHeaders.Add("Host", "www.tagsmaker.com");
// http.DefaultRequestHeaders.Add("Referer", "http://www.tagsmaker.com/");
// http.DefaultRequestHeaders.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
// http.DefaultRequestHeaders.Add("Alt-Used", "www.tagsmaker.com:443");
// var res = await http.GetAsync($"http://www.tagsmaker.com/tagsmaker.php?background_name=0011&tag_text={arg}&font_name=applejuiced&text_color=white&text_size=48&text_alignment=middle").ConfigureAwait(false);
// var img = res.RequestMessage.RequestUri.Segments[1].Replace("image-", "").Replace("tag-", "");
// var imgStream = await http.GetStreamAsync($"http://www.tagsmaker.com/upload/www.tagsmaker.com_{ img.ToString() }.png");
// var ms = new MemoryStream();
// await imgStream.CopyToAsync(ms).ConfigureAwait(false);
// ms.Position = 0;
// await channel.SendFileAsync(ms, arg+".png", "Provided by www.tagsmaker.com").ConfigureAwait(false);
// }
//}
} }
} }

View File

@ -17,6 +17,7 @@ using NadekoBot.TypeReaders;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using NadekoBot.Modules.Music; using NadekoBot.Modules.Music;
using NadekoBot.Services.Database.Models; using NadekoBot.Services.Database.Models;
using NadekoBot.Modules.Games.Commands.Hangman;
namespace NadekoBot namespace NadekoBot
{ {
@ -24,13 +25,12 @@ namespace NadekoBot
{ {
private Logger _log; private Logger _log;
public static uint OkColor { get; } = 0x00ff00; public static uint OkColor { get; } = 0x71cd40;
public static uint ErrorColor { get; } = 0xff0000; public static uint ErrorColor { get; } = 0xee281f;
public static CommandService CommandService { get; private set; } public static CommandService CommandService { get; private set; }
public static CommandHandler CommandHandler { get; private set; } public static CommandHandler CommandHandler { get; private set; }
public static ShardedDiscordClient Client { get; private set; } public static ShardedDiscordClient Client { get; private set; }
public static Localization Localizer { get; private set; }
public static BotCredentials Credentials { get; private set; } public static BotCredentials Credentials { get; private set; }
public static GoogleApiService Google { get; private set; } public static GoogleApiService Google { get; private set; }
@ -48,7 +48,7 @@ namespace NadekoBot
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
AllGuildConfigs = uow.GuildConfigs.GetAll(); AllGuildConfigs = uow.GuildConfigs.GetAllGuildConfigs();
} }
} }
@ -70,17 +70,16 @@ namespace NadekoBot
//initialize Services //initialize Services
CommandService = new CommandService(); CommandService = new CommandService();
Localizer = new Localization();
Google = new GoogleApiService(); Google = new GoogleApiService();
CommandHandler = new CommandHandler(Client, CommandService); CommandHandler = new CommandHandler(Client, CommandService);
Stats = new StatsService(Client, CommandHandler); Stats = new StatsService(Client, CommandHandler);
//setup DI ////setup DI
var depMap = new DependencyMap(); //var depMap = new DependencyMap();
depMap.Add<ILocalization>(Localizer); //depMap.Add<ILocalization>(Localizer);
depMap.Add<ShardedDiscordClient>(Client); //depMap.Add<ShardedDiscordClient>(Client);
depMap.Add<CommandService>(CommandService); //depMap.Add<CommandService>(CommandService);
depMap.Add<IGoogleApiService>(Google); //depMap.Add<IGoogleApiService>(Google);
//setup typereaders //setup typereaders
@ -104,9 +103,9 @@ namespace NadekoBot
// start handling messages received in commandhandler // start handling messages received in commandhandler
await CommandHandler.StartHandling().ConfigureAwait(false); await CommandHandler.StartHandling().ConfigureAwait(false);
await CommandService.LoadAssembly(this.GetType().GetTypeInfo().Assembly, depMap).ConfigureAwait(false); await CommandService.LoadAssembly(this.GetType().GetTypeInfo().Assembly).ConfigureAwait(false);
#if !GLOBAL_NADEKO #if !GLOBAL_NADEKO
await CommandService.Load(new Music(Localizer, CommandService, Client, Google)).ConfigureAwait(false); await CommandService.Load(new Music()).ConfigureAwait(false);
#endif #endif
Ready = true; Ready = true;
Console.WriteLine(await Stats.Print().ConfigureAwait(false)); Console.WriteLine(await Stats.Print().ConfigureAwait(false));

View File

@ -20,6 +20,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<DnxInvisibleContent Include="data\questions.json" /> <DnxInvisibleContent Include="data\questions.json" />
<DnxInvisibleContent Include="data\questions2.json" />
</ItemGroup> </ItemGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" /> <Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project> </Project>

Some files were not shown because too many files have changed in this diff Show More