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)
[![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
[![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
`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)
- [NSFW](#nsfw)
- [Permissions](#permissions)
- [Pokemon](#pokemon)
- [Searches](#searches)
- [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.`
`.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.`
`.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"`
`.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`
@ -63,13 +57,20 @@ Command and aliases | Description | Usage
`.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`
`.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`
`.userpresence` | Starts logging to this channel when someone from the server goes online/offline/idle. **Requires Administrator server permission.** | `.userpresence`
`.voicepresence` | Toggles logging to this channel whenever someone joins or leaves a voice channel you are currently in. **Requires Administrator server permission.** | `.voicepresence`
`.logevents` | Shows a list of all events you can subscribe to with `.log` **Requires Administrator server permission.** **Bot Owner only.** | `.logevents`
`.log` | Toggles logging event. Disables it if it's active anywhere on the server. Enables if it's not active. Use `.logevents` to see a 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`
`.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`
`.setmuterole` | Sets a name of the role which will be assigned to people who should be muted. Default is nadeko-mute. **Requires ManageRoles server permission.** | `.setmuterole Silenced`
`.mute` | Mutes a mentioned user both from speaking and chatting. **Requires ManageRoles server permission.** **Requires MuteMembers server permission.** | `.mute @Someone`
`.unmute` | Unmutes a mentioned user previously muted with `.mute` command. **Requires ManageRoles server permission.** **Requires MuteMembers server permission.** | `.unmute @Someone`
`.chatmute` | Prevents a mentioned user from chatting in text channels. **Requires ManageRoles server permission.** | `.chatmute @Someone`
`.chatunmute` | Removes a mute role previously set on a mentioned user with `.chatmute` which prevented him from chatting in text channels. **Requires ManageRoles server permission.** | `.chatunmute @Someone`
`.voicemute` | Prevents a mentioned user from speaking in voice channels. **Requires MuteMembers server permission.** | `.voicemute @Someone`
`.voiceunmute` | Gives a previously voice-muted user a permission to speak. **Requires MuteMembers server permission.** | `.voiceunmute @Someguy`
`.rotateplaying` `.ropl` | Toggles rotation of playing status of the dynamic strings you previously specified. **Bot Owner only.** | `.ropl`
`.addplaying` `.adpl` | Adds a specified string to the list of playing strings to rotate. Supported placeholders: %servers%, %users%, %playing%, %queued% **Bot Owner only.** | `.adpl`
`.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`
`.greetdmmsg` | Sets a new join announcement message which will be sent to the user who joined. Type %user% if you want to mention the new member. Using it with no message will show the current DM greet message. **Requires ManageServer server permission.** | `.greetdmmsg Welcome to the server, %user%`.
`.bye` | Toggles anouncements on the current channel when someone leaves the server. **Requires ManageServer server permission.** | `.bye`
`.byemsg` | Sets a new leave announcement message. Type %user% if you want to 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`
`.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`
@ -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`
`.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`
`.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)
@ -134,7 +137,7 @@ Command and aliases | Description | Usage
`$leaderboard` `$lb` | Displays bot currency leaderboard. | `$lb`
`$race` | Starts a new animal race. | `$race`
`$joinrace` `$jr` | Joins a new race. You can specify an amount of currency for betting (optional). You will get YourBet*(participants-1) back if you win. | `$jr` or `$jr 5`
`$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`
`$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`
@ -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`
`>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`
`>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`
`>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`
`>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`
@ -189,6 +195,7 @@ Command and aliases | Description | Usage
`!!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`
`!!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`
`!!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`
@ -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`
`!!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`
`!!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`
`!!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`
`!!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`
`!!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`
`!!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`
###### [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`
`~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`
`~yandere` | Shows a random image from yandere with a given tag. Tag is optional but preferred. (multiple tags are appended with +) | `~yandere tag1+tag2`
`~konachan` | Shows a random hentai image from konachan with a given tag. Tag is optional but preferred. | `~konachan yuri`
`~gelbooru` | Shows a random hentai image from gelbooru with a given tag. Tag is optional but preferred. (multiple tags are appended with +) | `~gelbooru yuri+kissing`
`~rule34` | Shows a random image from rule34.xx with a given tag. Tag is optional but preferred. (multiple tags are appended with +) | `~rule34 yuri+kissing`
@ -243,13 +250,13 @@ Command and aliases | Description | Usage
`;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`
`;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`
`;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`
`;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`
`;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`
`;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`
@ -268,10 +275,21 @@ Command and aliases | Description | Usage
###### [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
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`
`~imdb` `~omdb` | Queries omdb for movies or series, show first result. | `~imdb Batman vs Superman`
`~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`
`~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`
`~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`
`~define` `~def` | Finds a definition of a word. | `~def heresy`
`~#` | Searches Tagdef.com for a hashtag. | `~# ff`
`~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"`
@ -293,8 +313,6 @@ Command and aliases | Description | Usage
`~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"`
`~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`
`~minecraftping` `~mcping` | Pings a minecraft server. | `~mcping 127.0.0.1:25565`
`~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`
`~osub` | Shows information about an osu beatmap. | `~osub https://osu.ppy.sh/s/127712`
`~osu5` | Displays a user's top 5 plays. | `~osu5 Name`
`~overwatch` `~ow` | Show's basic stats on a player (competitive rank, playtime, level etc) Region codes are: `eu` `us` `cn` `kr` | `~ow us Battletag#1337` or `~overwatch eu Battletag#2016`
`~placelist` | Shows the list of available tags for the `~place` command. | `~placelist`
`~place` | Shows a placeholder image of a given tag. Use `~placelist` to see all available tags. You can specify the width and height of the image as the last two optional arguments. | `~place Cage` or `~place steven 500 400`
`~pokemon` `~poke` | Searches for a pokemon. | `~poke Sylveon`
@ -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`
`~beam` `~bm` | Notifies this channel when a certain user starts streaming. **Requires ManageMessages server permission.** | `~beam SomeStreamer`
`~liststreams` `~ls` | Lists all streams you are following on this server. | `~ls`
`~removestream` `~rms` | Removes notifications of a certain streamer 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`
`~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`
@ -332,6 +351,7 @@ Command and aliases | Description | Usage
### Utility
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`
`.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`
@ -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`
`.calculate` `.calc` | Evaluate a mathematical expression. | `.calc 1+1`
`.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`
`.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`
@ -354,7 +373,7 @@ Command and aliases | Description | Usage
`..` | Adds a new quote with the specified name and message. | `.. sayhi Hi`
`.deletequote` `.delq` | Deletes a random quote with the specified keyword. You have to either be server Administrator or the creator of the quote to delete it. | `.delq abc`
`.delallq` `.daq` | Deletes all quotes on a specified keyword. **Requires Administrator server permission.** | `.delallq kek`
`.remind` | Sends a message to you or a channel after certain amount of time. First argument is me/here/'channelname'. Second argument is time in a descending order (mo>w>d>h>m) example: 1w5d3h10m. Third argument is a (multiword)message. | `.remind me 1d5h Do something` or `.remind #general 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%!`
`.convertlist` | List of the convertible dimensions and currencies. | `.convertlist`
`.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.
###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!
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!"|
|`%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!".|
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`:
(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:

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`:
(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:

View File

@ -1,6 +1,69 @@
@ECHO off
@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
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<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");
@ -492,6 +522,22 @@ namespace NadekoBot.Migrations
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")
@ -658,6 +704,23 @@ namespace NadekoBot.Migrations
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")
@ -751,6 +814,13 @@ namespace NadekoBot.Migrations
.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")

View File

@ -15,6 +15,9 @@ using System.Net.Http;
using System.IO;
using static NadekoBot.Modules.Permissions.Permissions;
using System.Collections.Concurrent;
using NLog;
using NadekoBot.Services.Database;
using Microsoft.EntityFrameworkCore;
namespace NadekoBot.Modules.Administration
{
@ -24,27 +27,25 @@ namespace NadekoBot.Modules.Administration
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()
{
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));
}
_log = LogManager.GetCurrentClassLogger();
NadekoBot.CommandHandler.CommandExecuted += DelMsgOnCmd_Handler;
}
private async void DelMsgOnCmd_Handler(object sender, CommandExecutedEventArgs e)
private static async Task DelMsgOnCmd_Handler(IUserMessage msg, Command cmd)
{
try
{
var channel = e.Message.Channel as ITextChannel;
var channel = msg.Channel as ITextChannel;
if (channel == null)
return;
@ -52,11 +53,11 @@ namespace NadekoBot.Modules.Administration
bool shouldDelete;
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)
await e.Message.DeleteAsync().ConfigureAwait(false);
await msg.DeleteAsync().ConfigureAwait(false);
}
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]
[RequireContext(ContextType.Guild)]
[RequirePermission(GuildPermission.Administrator)]
@ -119,7 +87,7 @@ namespace NadekoBot.Modules.Administration
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]
@ -128,18 +96,18 @@ namespace NadekoBot.Modules.Administration
public async Task Delmsgoncmd(IUserMessage umsg)
{
var channel = (ITextChannel)umsg.Channel;
GuildConfig conf;
bool enabled;
using (var uow = DbHandler.UnitOfWork())
{
conf = uow.GuildConfigs.For(channel.Guild.Id);
conf.DeleteMessageOnCommand = !conf.DeleteMessageOnCommand;
uow.GuildConfigs.Update(conf);
var conf = uow.GuildConfigs.For(channel.Guild.Id, set => set);
enabled = conf.DeleteMessageOnCommand = !conf.DeleteMessageOnCommand;
await uow.CompleteAsync();
}
if (conf.DeleteMessageOnCommand)
await channel.SendMessageAsync("✅ **Now automatically deleting successful command invokations.**").ConfigureAwait(false);
if (enabled)
await channel.SendConfirmAsync("✅ **Now automatically deleting successful command invokations.**").ConfigureAwait(false);
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]
@ -151,11 +119,11 @@ namespace NadekoBot.Modules.Administration
try
{
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)
{
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());
}
}
@ -169,11 +137,11 @@ namespace NadekoBot.Modules.Administration
try
{
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
{
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))
{
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;
}
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)
{
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
{
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
{
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
{
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)
{
await channel.SendMessageAsync("⚠️ Unspecified error.").ConfigureAwait(false);
await channel.SendErrorAsync("⚠️ Unspecified error.").ConfigureAwait(false);
}
}
@ -247,15 +215,15 @@ namespace NadekoBot.Modules.Administration
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;
}
var roleName = args[0].ToUpperInvariant();
var role = channel.Guild.Roles.Where(r=>r.Name.ToUpperInvariant() == roleName).FirstOrDefault();
var role = channel.Guild.Roles.Where(r => r.Name.ToUpperInvariant() == roleName).FirstOrDefault();
if (role == null)
{
await channel.SendMessageAsync("🚫 That role **does not exist.**").ConfigureAwait(false);
await channel.SendErrorAsync("🚫 That role **does not exist.**").ConfigureAwait(false);
return;
}
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));
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)
{
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);
}
}
@ -286,14 +254,14 @@ namespace NadekoBot.Modules.Administration
{
msg = "❗No reason provided.";
}
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;
}
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);
await Task.Delay(2000).ConfigureAwait(false);
}
@ -302,11 +270,11 @@ namespace NadekoBot.Modules.Administration
{
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
{
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())
{
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;
}
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);
await Task.Delay(2000).ConfigureAwait(false);
}
@ -339,11 +307,11 @@ namespace NadekoBot.Modules.Administration
try { 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
{
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)
{
await channel.SendMessageAsync("❗User not found.").ConfigureAwait(false);
await channel.SendErrorAsync("❗User not found.").ConfigureAwait(false);
return;
}
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;
}
if (!string.IsNullOrWhiteSpace(msg))
{
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);
await Task.Delay(2000).ConfigureAwait(false);
}
@ -378,150 +346,11 @@ namespace NadekoBot.Modules.Administration
try
{
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
{
await channel.SendMessageAsync("⚠️ 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);
await channel.SendErrorAsync("⚠️ Error. Most likely I don't have sufficient permissions.").ConfigureAwait(false);
}
}
@ -538,13 +367,13 @@ namespace NadekoBot.Modules.Administration
{
foreach (var u in users)
{
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
{
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);
}
}
@ -561,13 +390,13 @@ namespace NadekoBot.Modules.Administration
{
foreach (var u in users)
{
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
{
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)
{
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]
@ -587,7 +416,7 @@ namespace NadekoBot.Modules.Administration
{
var channel = (ITextChannel)umsg.Channel;
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]
@ -596,7 +425,7 @@ namespace NadekoBot.Modules.Administration
public async Task DelTxtChanl(IUserMessage umsg, [Remainder] ITextChannel toDelete)
{
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]
@ -606,7 +435,7 @@ namespace NadekoBot.Modules.Administration
{
var channel = (ITextChannel)umsg.Channel;
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]
@ -617,7 +446,7 @@ namespace NadekoBot.Modules.Administration
var channel = (ITextChannel)umsg.Channel;
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]
@ -628,7 +457,7 @@ namespace NadekoBot.Modules.Administration
var channel = (ITextChannel)umsg.Channel;
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();
enumerable = enumerable.Where(x => x.Author.Id == user.Id);
await umsg.Channel.DeleteMessagesAsync(enumerable);
await umsg.Channel.DeleteMessagesAsync(enumerable).ConfigureAwait(false);
}
// prune x
@ -656,7 +485,7 @@ namespace NadekoBot.Modules.Administration
await (msg as IUserMessage).DeleteAsync();
int limit = (count < 100) ? count : 100;
var enumerable = (await msg.Channel.GetMessagesAsync(limit: limit));
await msg.Channel.DeleteMessagesAsync(enumerable);
await msg.Channel.DeleteMessagesAsync(enumerable).ConfigureAwait(false);
}
//prune @user [x]
@ -668,42 +497,34 @@ namespace NadekoBot.Modules.Administration
var channel = (ITextChannel)msg.Channel;
int limit = (count < 100) ? count : 100;
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]
[RequireContext(ContextType.Guild)]
[OwnerOnly]
public async Task Die(IUserMessage umsg)
{
var channel = (ITextChannel)umsg.Channel;
try { await channel.SendMessageAsync(" **Shutting down.**").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
try { await umsg.Channel.SendConfirmAsync(" **Shutting down.**").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
await Task.Delay(2000).ConfigureAwait(false);
Environment.Exit(0);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[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))
return;
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]
[RequireContext(ContextType.Guild)]
[OwnerOnly]
public async Task SetAvatar(IUserMessage umsg, [Remainder] string img = null)
{
var channel = (ITextChannel)umsg.Channel;
if (string.IsNullOrWhiteSpace(img))
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]
[RequireContext(ContextType.Guild)]
[OwnerOnly]
public async Task SetGame(IUserMessage umsg, [Remainder] string game = null)
{
var channel = (ITextChannel)umsg.Channel;
game = game ?? "";
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]
[RequireContext(ContextType.Guild)]
[OwnerOnly]
public async Task SetStream(IUserMessage umsg, string url, [Remainder] string name = null)
{
var channel = (ITextChannel)umsg.Channel;
name = name ?? "";
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]
[RequireContext(ContextType.Guild)]
[OwnerOnly]
public async Task Send(IUserMessage umsg, string where, [Remainder] string msg = null)
{
var channel = (ITextChannel)umsg.Channel;
if (string.IsNullOrWhiteSpace(msg))
return;
@ -791,25 +603,24 @@ namespace NadekoBot.Modules.Administration
}
else
{
await channel.SendMessageAsync("⚠️ Invalid format.").ConfigureAwait(false);
await umsg.Channel.SendErrorAsync("⚠️ Invalid format.").ConfigureAwait(false);
}
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[OwnerOnly]
public async Task Announce(IUserMessage umsg, [Remainder] string message)
{
var channel = (ITextChannel)umsg.Channel;
var channels = await Task.WhenAll(_client.GetGuilds().Select(g =>
var channels = await Task.WhenAll(NadekoBot.Client.GetGuilds().Select(g =>
g.GetDefaultChannelAsync()
)).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);
await channel.SendMessageAsync("🆗").ConfigureAwait(false);
await umsg.Channel.SendConfirmAsync("🆗").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
@ -839,8 +650,10 @@ namespace NadekoBot.Modules.Administration
cnt -= 100;
}
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 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);
}
@ -852,11 +665,11 @@ namespace NadekoBot.Modules.Administration
{
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)
{
send += $"\n**{role.Name}**\n";
send += string.Join(", ", (await channel.Guild.GetUsersAsync()).Where(u => u.Roles.Contains(role)).Distinct().Select(u=>u.Mention));
send += string.Join(", ", (await channel.Guild.GetUsersAsync()).Where(u => u.Roles.Contains(role)).Distinct().Select(u => u.Mention));
}
while (send.Length > 2000)
@ -872,19 +685,15 @@ namespace NadekoBot.Modules.Administration
IGuild nadekoSupportServer;
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Donators(IUserMessage umsg)
{
var channel = (ITextChannel)umsg.Channel;
IEnumerable<Donator> donatorsOrdered;
using (var uow = DbHandler.UnitOfWork())
{
donatorsOrdered = uow.Donators.GetDonatorsOrdered();
}
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);
await umsg.Channel.SendConfirmAsync("Thanks to the people listed below for making this project happen!", string.Join("⭐", donatorsOrdered.Select(d => d.Name))).ConfigureAwait(false);
nadekoSupportServer = nadekoSupportServer ?? NadekoBot.Client.GetGuild(117523346618318850);
@ -896,17 +705,14 @@ namespace NadekoBot.Modules.Administration
return;
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]
[RequireContext(ContextType.Guild)]
[OwnerOnly]
public async Task Donadd(IUserMessage umsg, IUser donator, int amount)
{
var channel = (ITextChannel)umsg.Channel;
Donator don;
using (var uow = DbHandler.UnitOfWork())
{
@ -914,7 +720,7 @@ namespace NadekoBot.Modules.Administration
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.Commands;
using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NLog;
using System;
using System.Collections.Concurrent;
@ -74,26 +75,24 @@ namespace NadekoBot.Modules.Administration
private static ConcurrentDictionary<ulong, AntiSpamSetting> antiSpamGuilds =
new ConcurrentDictionary<ulong, AntiSpamSetting>();
private Logger _log { get; }
private static Logger _log { get; }
public AntiRaidCommands(ShardedDiscordClient client)
static AntiRaidCommands()
{
_log = LogManager.GetCurrentClassLogger();
client.MessageReceived += (imsg) =>
NadekoBot.Client.MessageReceived += async (imsg) =>
{
try
{
var msg = imsg as IUserMessage;
if (msg == null || msg.Author.IsBot)
return Task.CompletedTask;
return;
var channel = msg.Channel as ITextChannel;
if (channel == null)
return Task.CompletedTask;
var t = Task.Run(async () =>
{
try
{
return;
AntiSpamSetting spamSettings;
if (!antiSpamGuilds.TryGetValue(channel.Guild.Id, out spamSettings))
return;
@ -105,27 +104,23 @@ namespace NadekoBot.Modules.Administration
{
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);
}
}
}
catch { }
});
return Task.CompletedTask;
};
client.UserJoined += (usr) =>
NadekoBot.Client.UserJoined += async (usr) =>
{
try
{
if (usr.IsBot)
return Task.CompletedTask;
return;
AntiRaidSetting settings;
if (!antiRaidGuilds.TryGetValue(usr.Guild.Id, out settings))
return Task.CompletedTask;
var t = Task.Run(async () =>
{
return;
if (!settings.RaidUsers.Add(usr))
return;
@ -136,19 +131,19 @@ namespace NadekoBot.Modules.Administration
var users = settings.RaidUsers.ToArray();
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);
settings.RaidUsers.TryRemove(usr);
--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)
{
@ -157,21 +152,21 @@ namespace NadekoBot.Modules.Administration
case PunishmentAction.Mute:
try
{
await gu.AddRolesAsync(muteRole);
await MuteCommands.Mute(gu).ConfigureAwait(false);
}
catch (Exception ex) { _log.Warn(ex, "I can't apply punishement"); }
break;
case PunishmentAction.Kick:
try
{
await gu.Guild.AddBanAsync(gu, 7);
await gu.Guild.AddBanAsync(gu, 7).ConfigureAwait(false);
try
{
await gu.Guild.RemoveBanAsync(gu);
await gu.Guild.RemoveBanAsync(gu).ConfigureAwait(false);
}
catch
{
await gu.Guild.RemoveBanAsync(gu);
await gu.Guild.RemoveBanAsync(gu).ConfigureAwait(false);
// try it twice, really don't want to ban user if
// only kick has been specified as the punishement
}
@ -181,7 +176,7 @@ namespace NadekoBot.Modules.Administration
case PunishmentAction.Ban:
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"); }
break;
@ -202,23 +197,23 @@ namespace NadekoBot.Modules.Administration
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;
}
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;
}
try
{
await GetMuteRole(channel.Guild).ConfigureAwait(false);
await MuteCommands.GetMuteRole(channel.Guild).ConfigureAwait(false);
}
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.")
.ConfigureAwait(false);
_log.Warn(ex);
@ -233,14 +228,14 @@ namespace NadekoBot.Modules.Administration
};
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);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[RequirePermission(GuildPermission.Administrator)]
public async Task AntiSpam(IUserMessage imsg, int messageCount=3, PunishmentAction action = PunishmentAction.Mute)
public async Task AntiSpam(IUserMessage imsg, int messageCount = 3, PunishmentAction action = PunishmentAction.Mute)
{
var channel = (ITextChannel)imsg.Channel;
@ -250,17 +245,17 @@ namespace NadekoBot.Modules.Administration
AntiSpamSetting 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
{
try
{
await GetMuteRole(channel.Guild).ConfigureAwait(false);
await MuteCommands.GetMuteRole(channel.Guild).ConfigureAwait(false);
}
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.")
.ConfigureAwait(false);
_log.Warn(ex);
@ -272,7 +267,7 @@ namespace NadekoBot.Modules.Administration
Action = action,
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.Commands;
using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Services;
using NadekoBot.Services.Database.Models;
using NLog;
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading.Tasks;
@ -15,35 +17,31 @@ namespace NadekoBot.Modules.Administration
[Group]
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;
this._log = LogManager.GetCurrentClassLogger();
_client.UserJoined += (user) =>
{
var t = Task.Run(async () =>
AutoAssignedRoles = new ConcurrentDictionary<ulong, ulong>(NadekoBot.AllGuildConfigs.Where(x => x.AutoAssignRoleId != 0)
.ToDictionary(k => k.GuildId, v => v.AutoAssignRoleId));
_log = LogManager.GetCurrentClassLogger();
NadekoBot.Client.UserJoined += async (user) =>
{
try
{
GuildConfig conf;
using (var uow = DbHandler.UnitOfWork())
{
conf = uow.GuildConfigs.For(user.Guild.Id);
}
ulong roleId = 0;
AutoAssignedRoles.TryGetValue(user.Guild.Id, out roleId);
if (conf.AutoAssignRoleId == 0)
if (roleId == 0)
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)
await user.AddRolesAsync(role);
await user.AddRolesAsync(role).ConfigureAwait(false);
}
catch (Exception ex) { _log.Warn(ex); }
});
return Task.CompletedTask;
};
}
@ -57,23 +55,29 @@ namespace NadekoBot.Modules.Administration
GuildConfig conf;
using (var uow = DbHandler.UnitOfWork())
{
conf = uow.GuildConfigs.For(channel.Guild.Id);
conf = uow.GuildConfigs.For(channel.Guild.Id, set => set);
if (role == null)
{
conf.AutoAssignRoleId = 0;
ulong throwaway;
AutoAssignedRoles.TryRemove(channel.Guild.Id, out throwaway);
}
else
{
conf.AutoAssignRoleId = role.Id;
AutoAssignedRoles.AddOrUpdate(channel.Guild.Id, role.Id, (key, val) => role.Id);
}
uow.GuildConfigs.Update(conf);
await uow.CompleteAsync().ConfigureAwait(false);
}
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;
}
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]
public class CrossServerTextChannel
{
public CrossServerTextChannel()
static CrossServerTextChannel()
{
_log = LogManager.GetCurrentClassLogger();
NadekoBot.Client.MessageReceived += (imsg) =>
NadekoBot.Client.MessageReceived += async (imsg) =>
{
try
{
if (imsg.Author.IsBot)
return Task.CompletedTask;
return;
var msg = imsg as IUserMessage;
if (msg == null)
return Task.CompletedTask;
return;
var channel = imsg.Channel as ITextChannel;
if (channel == null)
return Task.CompletedTask;
Task.Run(async () =>
{
return;
if (msg.Author.Id == NadekoBot.Client.GetCurrentUser().Id) return;
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); }
}
}
});
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;
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]
[RequireContext(ContextType.Guild)]
@ -67,7 +66,7 @@ namespace NadekoBot.Modules.Administration
if (Subscribers.TryAdd(token, set))
{
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))
return;
set.Add(channel);
await channel.SendMessageAsync(":ok:").ConfigureAwait(false);
await channel.SendConfirmAsync("Joined cross server channel.").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
@ -96,7 +95,7 @@ namespace NadekoBot.Modules.Administration
{
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.Commands;
using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Services;
using System.Collections.Generic;
using System.Linq;
@ -39,9 +40,9 @@ namespace NadekoBot.Modules.Administration
uow.Complete();
}
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
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]
@ -57,9 +58,9 @@ namespace NadekoBot.Modules.Administration
uow.Complete();
}
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
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())
{
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)
{
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
{
var firstOwnerChannel = ownerChannels.First();
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.WebSocket;
using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Services;
using NadekoBot.Services.Database.Models;
using NLog;
@ -18,7 +19,7 @@ namespace NadekoBot.Modules.Administration
[Group]
public class RepeatCommands
{
public ConcurrentDictionary<ulong, RepeatRunner> repeaters;
public static ConcurrentDictionary<ulong, RepeatRunner> repeaters { get; }
public class RepeatRunner
{
@ -49,10 +50,16 @@ namespace NadekoBot.Modules.Administration
{
while (!token.IsCancellationRequested)
{
var toSend = "🔄 " + Repeater.Message;
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)
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) { }
@ -70,7 +77,7 @@ namespace NadekoBot.Modules.Administration
}
}
public RepeatCommands()
static RepeatCommands()
{
using (var uow = DbHandler.UnitOfWork())
{
@ -88,7 +95,7 @@ namespace NadekoBot.Modules.Administration
RepeatRunner 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;
}
rep.Reset();
@ -110,10 +117,10 @@ namespace NadekoBot.Modules.Administration
await uow.CompleteAsync();
}
rep.Stop();
await channel.SendMessageAsync("✅ **Stopped repeating a message.**").ConfigureAwait(false);
await channel.SendConfirmAsync("✅ **Stopped repeating a message.**").ConfigureAwait(false);
}
else
await channel.SendMessageAsync(" **No message is repeating.**").ConfigureAwait(false);
await channel.SendConfirmAsync(" **No message is repeating.**").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
@ -159,7 +166,7 @@ namespace NadekoBot.Modules.Administration
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 grdel = false;
var byedel = grdel;
var gc = uow.GuildConfigs.For(gid);
var gc = uow.GuildConfigs.For(gid, set => set);
if (greetDM)
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 }));
selfAssRoles.AddRange(data.ListOfSelfAssignableRoles.Select(r => new SelfAssignedRole() { GuildId = guildConfig.GuildId, RoleId = r }).ToArray());
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.LogUserPresence = data.LogPresenceChannel != null;
guildConfig.LogSetting.UserPresenceChannelId = data.LogPresenceChannel ?? 0;
guildConfig.LogSetting.LogUserPresenceId = data.LogPresenceChannel;
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]
public class PlayingRotateCommands
{
private Logger _log { get; }
private static Logger _log { get; }
public static List<PlayingStatus> RotatingStatusMessages { get; }
public static bool RotatingStatuses { get; private set; } = false;
@ -30,12 +30,9 @@ namespace NadekoBot.Modules.Administration
RotatingStatusMessages = conf.RotatingStatusMessages;
RotatingStatuses = conf.RotatingStatuses;
}
}
public PlayingRotateCommands()
{
_log = LogManager.GetCurrentClassLogger();
Task.Run(async () =>
var t = Task.Run(async () =>
{
var index = 0;
do
@ -90,12 +87,9 @@ namespace NadekoBot.Modules.Administration
};
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[OwnerOnly]
public async Task RotatePlaying(IUserMessage umsg)
{
var channel = (ITextChannel)umsg.Channel;
using (var uow = DbHandler.UnitOfWork())
{
var config = uow.BotConfig.GetOrCreate();
@ -104,18 +98,15 @@ namespace NadekoBot.Modules.Administration
await uow.CompleteAsync();
}
if (RotatingStatuses)
await channel.SendMessageAsync("🆗 **Rotating playing status enabled.**");
await umsg.Channel.SendConfirmAsync("🆗 **Rotating playing status enabled.**").ConfigureAwait(false);
else
await channel.SendMessageAsync(" **Rotating playing status disabled.**");
await umsg.Channel.SendConfirmAsync(" **Rotating playing status disabled.**").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[OwnerOnly]
public async Task AddPlaying(IUserMessage umsg, [Remainder] string status)
{
var channel = (ITextChannel)umsg.Channel;
using (var uow = DbHandler.UnitOfWork())
{
var config = uow.BotConfig.GetOrCreate();
@ -125,33 +116,27 @@ namespace NadekoBot.Modules.Administration
await uow.CompleteAsync();
}
await channel.SendMessageAsync("✅ **Added.**").ConfigureAwait(false);
await umsg.Channel.SendConfirmAsync("✅ **Added.**").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[OwnerOnly]
public async Task ListPlaying(IUserMessage umsg)
{
var channel = (ITextChannel)umsg.Channel;
if (!RotatingStatusMessages.Any())
await channel.SendMessageAsync("❎ **No rotating playing statuses set.**");
await umsg.Channel.SendErrorAsync("❎ **No rotating playing statuses set.**");
else
{
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]
[RequireContext(ContextType.Guild)]
[OwnerOnly]
public async Task RemovePlaying(IUserMessage umsg, int index)
{
var channel = (ITextChannel)umsg.Channel;
index -= 1;
string msg = "";
@ -166,7 +151,7 @@ namespace NadekoBot.Modules.Administration
RotatingStatusMessages.RemoveAt(index);
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 static ConcurrentDictionary<ulong, Ratelimiter> RatelimitingChannels = new ConcurrentDictionary<ulong, Ratelimiter>();
private Logger _log { get; }
private ShardedDiscordClient _client { get; }
private static Logger _log { get; }
public class Ratelimiter
{
@ -47,7 +45,8 @@ namespace NadekoBot.Modules.Administration
else
{
usr.MessageCount++;
var t = Task.Run(async () => {
var t = Task.Run(async () =>
{
try
{
await Task.Delay(PerSeconds * 1000, cancelSource.Token);
@ -61,16 +60,17 @@ namespace NadekoBot.Modules.Administration
}
}
public RatelimitCommand()
static RatelimitCommand()
{
this._client = NadekoBot.Client;
this._log = LogManager.GetCurrentClassLogger();
_log = LogManager.GetCurrentClassLogger();
_client.MessageReceived += (umsg) =>
NadekoBot.Client.MessageReceived += async (umsg) =>
{
var t = Task.Run(async () =>
try
{
var usrMsg = umsg as IUserMessage;
if (usrMsg == null)
return;
var channel = usrMsg.Channel as ITextChannel;
if (channel == null || usrMsg.IsAuthor())
@ -80,9 +80,9 @@ namespace NadekoBot.Modules.Administration
return;
if (limiter.CheckUserRatelimit(usrMsg.Author.Id))
try { await usrMsg.DeleteAsync(); } catch (Exception ex) { _log.Warn(ex); }
});
return Task.CompletedTask;
await usrMsg.DeleteAsync();
}
catch (Exception ex) { _log.Warn(ex); }
};
}
@ -97,7 +97,7 @@ namespace NadekoBot.Modules.Administration
if (RatelimitingChannels.TryRemove(channel.Id, out throwaway))
{
throwaway.cancelSource.Cancel();
await channel.SendMessageAsync(" **Slow mode disabled.**").ConfigureAwait(false);
await channel.SendConfirmAsync(" Slow mode disabled.").ConfigureAwait(false);
return;
}
}
@ -112,7 +112,7 @@ namespace NadekoBot.Modules.Administration
if (msg < 1 || perSec < 1 || msg > 100 || perSec > 3600)
{
await channel.SendMessageAsync("⚠️ `Invalid parameters.`");
await channel.SendErrorAsync("⚠️ Invalid parameters.");
return;
}
var toAdd = new Ratelimiter()
@ -121,10 +121,10 @@ namespace NadekoBot.Modules.Administration
MaxMessages = msg,
PerSeconds = perSec,
};
if(RatelimitingChannels.TryAdd(channel.Id, toAdd))
if (RatelimitingChannels.TryAdd(channel.Id, toAdd))
{
await channel.SendMessageAsync("✅ **Slow mode initiated: " +
$"Users can't send more than `{toAdd.MaxMessages} message(s)` every `{toAdd.PerSeconds} second(s)`.**")
await channel.SendConfirmAsync("Slow mode initiated",
$"Users can't send more than `{toAdd.MaxMessages} message(s)` every `{toAdd.PerSeconds} second(s)`.")
.ConfigureAwait(false);
}
}

View File

@ -1,6 +1,7 @@
using Discord;
using Discord.Commands;
using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Services;
using NadekoBot.Services.Database.Models;
using System;
@ -27,12 +28,12 @@ namespace NadekoBot.Modules.Administration
bool newval;
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;
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);
}
@ -51,7 +52,8 @@ namespace NadekoBot.Modules.Administration
roles = uow.SelfAssignedRoles.GetFromGuild(channel.Guild.Id);
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
{
@ -63,7 +65,7 @@ namespace NadekoBot.Modules.Administration
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]
@ -81,10 +83,10 @@ namespace NadekoBot.Modules.Administration
}
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;
}
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]
@ -96,10 +98,12 @@ namespace NadekoBot.Modules.Administration
var toRemove = new ConcurrentHashSet<SelfAssignedRole>();
var removeMsg = new StringBuilder();
var msg = new StringBuilder();
var roleCnt = 0;
using (var uow = DbHandler.UnitOfWork())
{
var roleModels = uow.SelfAssignedRoles.GetFromGuild(channel.Guild.Id);
msg.AppendLine($" There are `{roleModels.Count()}` self assignable roles:");
var roleModels = uow.SelfAssignedRoles.GetFromGuild(channel.Guild.Id).ToList();
roleCnt = roleModels.Count;
msg.AppendLine();
foreach (var roleModel in roleModels)
{
@ -119,7 +123,7 @@ namespace NadekoBot.Modules.Administration
}
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]
@ -132,13 +136,13 @@ namespace NadekoBot.Modules.Administration
bool areExclusive;
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;
await uow.CompleteAsync();
}
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]
@ -153,18 +157,18 @@ namespace NadekoBot.Modules.Administration
IEnumerable<SelfAssignedRole> roles;
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);
}
SelfAssignedRole roleModel;
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;
}
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;
}
@ -173,7 +177,7 @@ namespace NadekoBot.Modules.Administration
var sameRoles = guildUser.Roles.Where(r => roles.Any(rm => rm.RoleId == r.Id));
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;
}
}
@ -183,20 +187,16 @@ namespace NadekoBot.Modules.Administration
}
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);
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)
{
var t = Task.Run(async () =>
{
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 { }
});
msg.DeleteAfter(3);
umsg.DeleteAfter(3);
}
}
@ -207,22 +207,22 @@ namespace NadekoBot.Modules.Administration
var channel = (ITextChannel)umsg.Channel;
var guildUser = (IGuildUser)umsg.Author;
GuildConfig conf;
bool autoDeleteSelfAssignedRoleMessages;
IEnumerable<SelfAssignedRole> roles;
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);
}
SelfAssignedRole roleModel;
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;
}
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;
}
try
@ -231,19 +231,15 @@ namespace NadekoBot.Modules.Administration
}
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;
}
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 () =>
{
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 { }
});
msg.DeleteAfter(3);
umsg.DeleteAfter(3);
}
}
}

View File

@ -1,6 +1,7 @@
using Discord;
using Discord.Commands;
using NadekoBot.Attributes;
using NadekoBot.Extensions;
using System.Linq;
using System.Threading.Tasks;
@ -13,9 +14,9 @@ namespace NadekoBot.Modules.Administration
{
private ShardedDiscordClient _client;
public SelfCommands(ShardedDiscordClient client)
public SelfCommands()
{
this._client = client;
this._client = NadekoBot.Client;
}
[NadekoCommand, Usage, Description, Aliases]
@ -31,18 +32,18 @@ namespace NadekoBot.Modules.Administration
if (server == null)
{
await channel.SendMessageAsync("⚠️ Cannot find that server").ConfigureAwait(false);
await channel.SendErrorAsync("⚠️ Cannot find that server").ConfigureAwait(false);
return;
}
if (server.OwnerId != _client.GetCurrentUser().Id)
{
await server.LeaveAsync().ConfigureAwait(false);
await channel.SendMessageAsync("✅ Left server " + server.Name).ConfigureAwait(false);
await channel.SendConfirmAsync("✅ Left server " + server.Name).ConfigureAwait(false);
}
else
{
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]
public class ServerGreetCommands
{
public static long Greeted = 0;
private Logger _log;
private static Logger _log { get; }
public ServerGreetCommands()
static ServerGreetCommands()
{
NadekoBot.Client.UserJoined += UserJoined;
NadekoBot.Client.UserLeft += UserLeft;
_log = LogManager.GetCurrentClassLogger();
}
private Task UserLeft(IGuildUser user)
{
var leftTask = Task.Run(async () =>
//todo optimize ASAP
private static async void UserLeft(IGuildUser user)
{
try
{
GuildConfig conf;
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;
@ -53,30 +50,22 @@ namespace NadekoBot.Modules.Administration
var toDelete = await channel.SendMessageAsync(msg.SanitizeMentions()).ConfigureAwait(false);
if (conf.AutoDeleteByeMessagesTimer > 0)
{
var t = Task.Run(async () =>
{
await Task.Delay(conf.AutoDeleteByeMessagesTimer * 1000).ConfigureAwait(false); // 5 minutes
try { await toDelete.DeleteAsync().ConfigureAwait(false); } catch { }
});
toDelete.DeleteAfter(conf.AutoDeleteByeMessagesTimer);
}
}
catch (Exception ex) { _log.Warn(ex); }
}
catch { }
});
return Task.CompletedTask;
}
private Task UserJoined(IGuildUser user)
{
var joinedTask = Task.Run(async () =>
private static async void UserJoined(IGuildUser user)
{
try
{
GuildConfig conf;
using (var uow = DbHandler.UnitOfWork())
{
conf = uow.GuildConfigs.For(user.Guild.Id);
conf = uow.GuildConfigs.For(user.Guild.Id, set => set);
}
if (conf.SendChannelGreetMessage)
@ -92,11 +81,7 @@ namespace NadekoBot.Modules.Administration
var toDelete = await channel.SendMessageAsync(msg.SanitizeMentions()).ConfigureAwait(false);
if (conf.AutoDeleteGreetMessagesTimer > 0)
{
var t = Task.Run(async () =>
{
await Task.Delay(conf.AutoDeleteGreetMessagesTimer * 1000).ConfigureAwait(false); // 5 minutes
try { await toDelete.DeleteAsync().ConfigureAwait(false); } catch { }
});
toDelete.DeleteAfter(conf.AutoDeleteGreetMessagesTimer);
}
}
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);
if (!string.IsNullOrWhiteSpace(msg))
{
await channel.SendMessageAsync(msg).ConfigureAwait(false);
await channel.SendConfirmAsync(msg).ConfigureAwait(false);
}
}
}
}
catch { }
});
return Task.CompletedTask;
}
[NadekoCommand, Usage, Description, Aliases]
@ -135,9 +118,9 @@ namespace NadekoBot.Modules.Administration
await ServerGreetCommands.SetGreetDel(channel.Guild.Id, timer).ConfigureAwait(false);
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
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)
@ -147,9 +130,9 @@ namespace NadekoBot.Modules.Administration
using (var uow = DbHandler.UnitOfWork())
{
var conf = uow.GuildConfigs.For(id);
var conf = uow.GuildConfigs.For(id, set => set);
conf.AutoDeleteGreetMessagesTimer = timer;
uow.GuildConfigs.Update(conf);
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);
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
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)
@ -174,10 +157,10 @@ namespace NadekoBot.Modules.Administration
bool enabled;
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;
conf.GreetMessageChannelId = channelId;
uow.GuildConfigs.Update(conf);
await uow.CompleteAsync().ConfigureAwait(false);
}
return enabled;
@ -192,20 +175,20 @@ namespace NadekoBot.Modules.Administration
if (string.IsNullOrWhiteSpace(text))
{
GuildConfig config;
string channelGreetMessageText;
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;
}
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)
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)
@ -218,11 +201,10 @@ namespace NadekoBot.Modules.Administration
bool greetMsgEnabled;
using (var uow = DbHandler.UnitOfWork())
{
var conf = uow.GuildConfigs.For(guildId);
var conf = uow.GuildConfigs.For(guildId, set => set);
conf.ChannelGreetMessageText = message;
greetMsgEnabled = conf.SendChannelGreetMessage;
uow.GuildConfigs.Update(conf);
uow.Complete();
}
return greetMsgEnabled;
@ -238,9 +220,9 @@ namespace NadekoBot.Modules.Administration
var enabled = await ServerGreetCommands.SetGreetDm(channel.Guild.Id).ConfigureAwait(false);
if (enabled)
await channel.SendMessageAsync("🆗 DM Greet announcements **enabled**.").ConfigureAwait(false);
await channel.SendConfirmAsync("🆗 DM Greet announcements **enabled**.").ConfigureAwait(false);
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)
@ -248,9 +230,9 @@ namespace NadekoBot.Modules.Administration
bool enabled;
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;
uow.GuildConfigs.Update(conf);
await uow.CompleteAsync().ConfigureAwait(false);
}
return enabled;
@ -270,15 +252,15 @@ namespace NadekoBot.Modules.Administration
{
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;
}
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)
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)
@ -295,7 +277,6 @@ namespace NadekoBot.Modules.Administration
conf.DmGreetMessageText = message;
greetMsgEnabled = conf.SendDmGreetMessage;
uow.GuildConfigs.Update(conf);
uow.Complete();
}
return greetMsgEnabled;
@ -311,9 +292,9 @@ namespace NadekoBot.Modules.Administration
var enabled = await ServerGreetCommands.SetBye(channel.Guild.Id, channel.Id).ConfigureAwait(false);
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
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)
@ -321,10 +302,10 @@ namespace NadekoBot.Modules.Administration
bool enabled;
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;
conf.ByeMessageChannelId = channelId;
uow.GuildConfigs.Update(conf);
await uow.CompleteAsync();
}
return enabled;
@ -339,20 +320,20 @@ namespace NadekoBot.Modules.Administration
if (string.IsNullOrWhiteSpace(text))
{
GuildConfig config;
string byeMessageText;
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;
}
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)
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)
@ -365,11 +346,10 @@ namespace NadekoBot.Modules.Administration
bool byeMsgEnabled;
using (var uow = DbHandler.UnitOfWork())
{
var conf = uow.GuildConfigs.For(guildId);
var conf = uow.GuildConfigs.For(guildId, set => set);
conf.ChannelByeMessageText = message;
byeMsgEnabled = conf.SendChannelByeMessage;
uow.GuildConfigs.Update(conf);
uow.Complete();
}
return byeMsgEnabled;
@ -385,9 +365,9 @@ namespace NadekoBot.Modules.Administration
await ServerGreetCommands.SetByeDel(channel.Guild.Id, timer).ConfigureAwait(false);
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
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)
@ -397,9 +377,9 @@ namespace NadekoBot.Modules.Administration
using (var uow = DbHandler.UnitOfWork())
{
var conf = uow.GuildConfigs.For(id);
var conf = uow.GuildConfigs.For(id, set => set);
conf.AutoDeleteByeMessagesTimer = timer;
uow.GuildConfigs.Update(conf);
await uow.CompleteAsync().ConfigureAwait(false);
}
}

View File

@ -17,10 +17,10 @@ namespace NadekoBot.Modules.Administration
[Group]
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;
public VoicePlusTextCommands()
private static ConcurrentHashSet<ulong> voicePlusTextCache { get; }
static VoicePlusTextCommands()
{
using (var uow = DbHandler.UnitOfWork())
{
@ -29,15 +29,14 @@ namespace NadekoBot.Modules.Administration
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 guild = user?.Guild;
if (guild == null)
return Task.CompletedTask;
var task = Task.Run(async () =>
{
return;
try
{
var botUserPerms = guild.GetCurrentUser().GuildPermissions;
@ -51,14 +50,14 @@ namespace NadekoBot.Modules.Administration
{
try
{
await (await guild.GetOwnerAsync()).SendMessageAsync(
await (await guild.GetOwnerAsync()).SendErrorAsync(
"⚠️ I don't have **manage server** and/or **manage channels** permission," +
$" so I cannot run `voice+text` on **{guild.Name}** server.").ConfigureAwait(false);
}
catch { }
using (var uow = DbHandler.UnitOfWork())
{
uow.GuildConfigs.For(guild.Id).VoicePlusTextEnabled = false;
uow.GuildConfigs.For(guild.Id, set => set).VoicePlusTextEnabled = false;
voicePlusTextCache.TryRemove(guild.Id);
await uow.CompleteAsync().ConfigureAwait(false);
}
@ -97,11 +96,9 @@ namespace NadekoBot.Modules.Administration
{
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";
[NadekoCommand, Usage, Description, Aliases]
@ -116,7 +113,7 @@ namespace NadekoBot.Modules.Administration
var botUser = await guild.GetCurrentUserAsync().ConfigureAwait(false);
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;
}
@ -124,7 +121,7 @@ namespace NadekoBot.Modules.Administration
{
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.`");
}
catch { }
@ -134,7 +131,7 @@ namespace NadekoBot.Modules.Administration
bool isEnabled;
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;
await uow.CompleteAsync().ConfigureAwait(false);
}
@ -145,16 +142,16 @@ namespace NadekoBot.Modules.Administration
{
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;
}
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)
{
await channel.SendMessageAsync(ex.ToString()).ConfigureAwait(false);
await channel.SendErrorAsync(ex.ToString()).ConfigureAwait(false);
}
}
[NadekoCommand, Usage, Description, Aliases]
@ -168,7 +165,7 @@ namespace NadekoBot.Modules.Administration
var botUser = await guild.GetCurrentUserAsync().ConfigureAwait(false);
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;
}
@ -183,7 +180,7 @@ namespace NadekoBot.Modules.Administration
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 NadekoBot.Services.Database.Models;
using System.Linq;
using NadekoBot.Extensions;
using System.Threading;
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>>();
private static Timer checkWarTimer { get; }
static ClashOfClans()
{
using (var uow = DbHandler.UnitOfWork())
@ -31,13 +35,21 @@ namespace NadekoBot.Modules.ClashOfClans
?.GetTextChannel(cw.ChannelId);
return cw;
})
.Where(cw => cw?.Channel != null)
.Where(cw => cw.Channel != null)
.GroupBy(cw => cw.GuildId)
.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)
@ -45,11 +57,20 @@ namespace NadekoBot.Modules.ClashOfClans
var Bases = war.Bases;
for (var i = 0; i < Bases.Count; i++)
{
if (Bases[i].CallUser == null) continue;
if (!Bases[i].BaseDestroyed && DateTime.UtcNow - Bases[i].TimeAdded >= callExpire)
var callUser = Bases[i].CallUser;
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;
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)
{
await channel.SendMessageAsync("💢🔰 Not a Valid war size").ConfigureAwait(false);
await channel.SendErrorAsync("🔰 Not a Valid war size").ConfigureAwait(false);
return;
}
List<ClashWar> wars;
@ -83,7 +104,7 @@ namespace NadekoBot.Modules.ClashOfClans
var cw = await CreateWar(enemyClan, size, channel.Guild.Id, umsg.Channel.Id);
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]
@ -98,18 +119,18 @@ namespace NadekoBot.Modules.ClashOfClans
var warsInfo = GetWarInfo(umsg, num);
if (warsInfo == null)
{
await channel.SendMessageAsync("💢🔰 **That war does not exist.**").ConfigureAwait(false);
await channel.SendErrorAsync("🔰 **That war does not exist.**").ConfigureAwait(false);
return;
}
var war = warsInfo.Item1[warsInfo.Item2];
try
{
war.Start();
await channel.SendMessageAsync($"🔰**STARTED WAR AGAINST {war.ShortPrint()}**").ConfigureAwait(false);
await channel.SendConfirmAsync($"🔰**STARTED WAR AGAINST {war.ShortPrint()}**").ConfigureAwait(false);
}
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);
}
@ -128,7 +149,7 @@ namespace NadekoBot.Modules.ClashOfClans
ClashWars.TryGetValue(channel.Guild.Id, out wars);
if (wars == null || wars.Count == 0)
{
await channel.SendMessageAsync("🔰 **No active wars.**").ConfigureAwait(false);
await channel.SendErrorAsync("🔰 **No active wars.**").ConfigureAwait(false);
return;
}
@ -141,7 +162,7 @@ namespace NadekoBot.Modules.ClashOfClans
sb.AppendLine($"\t\t`Size:` **{wars[i].Size} v {wars[i].Size}**");
sb.AppendLine("**-------------------------**");
}
await channel.SendMessageAsync(sb.ToString()).ConfigureAwait(false);
await channel.SendConfirmAsync(sb.ToString()).ConfigureAwait(false);
return;
}
@ -151,10 +172,10 @@ namespace NadekoBot.Modules.ClashOfClans
var warsInfo = GetWarInfo(umsg, num);
if (warsInfo == null)
{
await channel.SendMessageAsync("💢🔰 **That war does not exist.**").ConfigureAwait(false);
await channel.SendErrorAsync("🔰 **That war does not exist.**").ConfigureAwait(false);
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]
@ -165,7 +186,7 @@ namespace NadekoBot.Modules.ClashOfClans
var warsInfo = GetWarInfo(umsg, number);
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;
}
var usr =
@ -177,11 +198,11 @@ namespace NadekoBot.Modules.ClashOfClans
var war = warsInfo.Item1[warsInfo.Item2];
war.Call(usr, baseNumber - 1);
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)
{
await channel.SendMessageAsync($"💢🔰 {ex.Message}").ConfigureAwait(false);
await channel.SendErrorAsync($"🔰 {ex.Message}").ConfigureAwait(false);
}
}
@ -215,16 +236,16 @@ namespace NadekoBot.Modules.ClashOfClans
{
var channel = (ITextChannel)umsg.Channel;
var warsInfo = GetWarInfo(umsg,number);
var warsInfo = GetWarInfo(umsg, number);
if (warsInfo == null)
{
await channel.SendMessageAsync("💢🔰 That war does not exist.").ConfigureAwait(false);
await channel.SendErrorAsync("🔰 That war does not exist.").ConfigureAwait(false);
return;
}
var war = warsInfo.Item1[warsInfo.Item2];
war.End();
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;
warsInfo.Item1.RemoveAt(warsInfo.Item2);
@ -239,7 +260,7 @@ namespace NadekoBot.Modules.ClashOfClans
var warsInfo = GetWarInfo(umsg, number);
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;
}
var usr =
@ -251,11 +272,11 @@ namespace NadekoBot.Modules.ClashOfClans
var war = warsInfo.Item1[warsInfo.Item2];
var baseNumber = war.Uncall(usr);
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)
{
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);
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;
}
var war = warInfo.Item1[warInfo.Item2];
@ -280,11 +301,11 @@ namespace NadekoBot.Modules.ClashOfClans
{
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)
{
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)
throw new ArgumentException("Invalid base number");
if (cw.Bases[baseNumber].CallUser != null)
throw new ArgumentException("That base is already claimed.");
if (cw.Bases[baseNumber].CallUser != null && cw.Bases[baseNumber].Stars == 3)
throw new ArgumentException("That base is already destroyed.");
for (var i = 0; i < cw.Bases.Count; i++)
{
if (cw.Bases[i]?.BaseDestroyed == false && cw.Bases[i]?.CallUser == u)
@ -95,8 +95,15 @@ namespace NadekoBot.Modules.ClashOfClans
else
{
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");
}
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

@ -10,12 +10,14 @@ using NadekoBot.Extensions;
namespace NadekoBot.Modules.CustomReactions
{
[NadekoModule("CustomReactions",".")]
[NadekoModule("CustomReactions", ".")]
public class CustomReactions : DiscordModule
{
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<string, uint> ReactionStats { get; } = new ConcurrentDictionary<string, uint>();
static CustomReactions()
{
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));
}
}
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)
{
var channel = umsg.Channel as ITextChannel;
@ -40,14 +44,18 @@ namespace NadekoBot.Modules.CustomReactions
GuildReactions.TryGetValue(channel.Guild.Id, out reactions);
if (reactions != null && reactions.Any())
{
var reaction = reactions.Where(cr => {
var reaction = reactions.Where(cr =>
{
var hasTarget = cr.Response.ToLowerInvariant().Contains("%target%");
var trigger = cr.TriggerWithContext(umsg).Trim().ToLowerInvariant();
return ((hasTarget && content.StartsWith(trigger + " ")) || content == trigger);
}).Shuffle().FirstOrDefault();
if (reaction != null)
{
if (reaction.Response != "-")
try { await channel.SendMessageAsync(reaction.ResponseWithContext(umsg)).ConfigureAwait(false); } catch { }
ReactionStats.AddOrUpdate(reaction.Trigger, 1, (k, old) => ++old);
return true;
}
}
@ -61,6 +69,7 @@ namespace NadekoBot.Modules.CustomReactions
if (greaction != null)
{
try { await channel.SendMessageAsync(greaction.ResponseWithContext(umsg)).ConfigureAwait(false); } catch { }
ReactionStats.AddOrUpdate(greaction.Trigger, 1, (k, old) => ++old);
return true;
}
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))
{
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;
}
@ -106,7 +115,12 @@ namespace NadekoBot.Modules.CustomReactions
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]
@ -124,12 +138,11 @@ namespace NadekoBot.Modules.CustomReactions
customReactions = GuildReactions.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet<CustomReaction>());
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
await imsg.Channel.SendMessageAsync(
$"`Page {page} of custom reactions:`\n" +
string.Join("\n", customReactions
.OrderBy(cr => cr.Trigger)
await imsg.Channel.SendConfirmAsync(
$"Page {page} of custom reactions:",
string.Join("\n", customReactions.OrderBy(cr => cr.Trigger)
.Skip((page - 1) * 20)
.Take(20)
.Select(cr => $"`#{cr.Id}` `Trigger:` {cr.Trigger}")))
@ -154,12 +167,12 @@ namespace NadekoBot.Modules.CustomReactions
customReactions = GuildReactions.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet<CustomReaction>());
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
{
var txtStream = await customReactions.GroupBy(cr => cr.Trigger)
.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()
.ToStream()
.ConfigureAwait(false);
@ -183,11 +196,11 @@ namespace NadekoBot.Modules.CustomReactions
customReactions = GuildReactions.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet<CustomReaction>());
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
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
.GroupBy(cr=>cr.Trigger)
.GroupBy(cr => cr.Trigger)
.OrderBy(cr => cr.Key)
.Skip((page - 1) * 20)
.Take(20)
@ -209,11 +222,14 @@ namespace NadekoBot.Modules.CustomReactions
var found = customReactions.FirstOrDefault(cr => cr.Id == id);
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
{
await imsg.Channel.SendMessageAsync($"`Custom reaction #{id}`\n`Trigger:` {found.Trigger}\n`Response:` {found.Response} ```css\n{found.Response}```")
.ConfigureAwait(false);
await imsg.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
.WithDescription($"#{id}")
.AddField(efb => efb.WithName("Trigger").WithValue(found.Trigger))
.AddField(efb => efb.WithName("Response").WithValue(found.Response + "\n```css\n" + found.Response + "```"))
.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))
{
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;
}
@ -248,14 +264,50 @@ namespace NadekoBot.Modules.CustomReactions
GuildReactions.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet<CustomReaction>()).RemoveWhere(cr => cr.Id == toDelete.Id);
success = true;
}
if(success)
if (success)
await uow.CompleteAsync().ConfigureAwait(false);
}
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
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 NadekoBot.Extensions;
using NadekoBot.Services;
using NadekoBot.Services.Database.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
namespace NadekoBot.Modules.CustomReactions
{
@ -17,7 +20,41 @@ namespace NadekoBot.Modules.CustomReactions
{
{"%mention%", (ctx) => { return $"<@{NadekoBot.Client.GetCurrentUser().Id}>"; } },
{"%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)
@ -40,6 +77,11 @@ namespace NadekoBot.Modules.CustomReactions
{
str = str.Replace(ph.Key.ToLowerInvariant(), ph.Value(ctx, resolvedTrigger));
}
foreach (var ph in regexPlaceholders)
{
str = ph.Key.Replace(str, ph.Value);
}
return str;
}

View File

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

View File

@ -18,11 +18,7 @@ namespace NadekoBot.Modules.Gambling
[Group]
public class AnimalRacing
{
public AnimalRacing()
{
}
public static ConcurrentDictionary<ulong, AnimalRace> AnimalRaces = new ConcurrentDictionary<ulong, AnimalRace>();
public static ConcurrentDictionary<ulong, AnimalRace> AnimalRaces { get; } = new ConcurrentDictionary<ulong, AnimalRace>();
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
@ -33,7 +29,7 @@ namespace NadekoBot.Modules.Gambling
var ar = new AnimalRace(channel.Guild.Id, channel);
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]
@ -49,7 +45,7 @@ namespace NadekoBot.Modules.Gambling
AnimalRace 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;
}
await ar.JoinRace(umsg.Author as IGuildUser, amount);
@ -94,21 +90,29 @@ namespace NadekoBot.Modules.Gambling
{
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);
Started = true;
cancelSource.Cancel();
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)
{
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
{
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();
if (p != null && p.AmountBet > 0)
@ -119,7 +123,7 @@ namespace NadekoBot.Modules.Gambling
await Task.Run(StartRace);
End();
}
catch { }
catch { try { End(); } catch { } }
});
}
@ -145,13 +149,20 @@ namespace NadekoBot.Modules.Gambling
participants.ForEach(p =>
{
p.Total += 1 + rng.Next(0, 10);
});
participants
.OrderByDescending(p => p.Total)
.ForEach(p =>
{
if (p.Total > 60)
{
p.Total = 60;
if (winner == null)
{
winner = p;
}
p.Total = 60;
if (p.Place == 0)
p.Place = place++;
}
@ -188,25 +199,25 @@ namespace NadekoBot.Modules.Gambling
{
var wonAmount = winner.AmountBet * (participants.Count - 1);
await CurrencyHandler.AddCurrencyAsync(winner.User, "Won a Race", wonAmount, false).ConfigureAwait(false);
await raceChannel.SendMessageAsync($"🏁 {winner.User.Mention} as {winner.Animal} **Won the race and {wonAmount}{CurrencySign}!**").ConfigureAwait(false);
await CurrencyHandler.AddCurrencyAsync(winner.User, "Won a Race", wonAmount, true).ConfigureAwait(false);
await raceChannel.SendConfirmAsync("Animal Race", $"{winner.User.Mention} as {winner.Animal} **Won the race and {wonAmount}{CurrencySign}!**").ConfigureAwait(false);
}
else
{
await raceChannel.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;
if (msg == null)
return Task.CompletedTask;
return;
if (msg.IsAuthor() || !(imsg.Channel is ITextChannel) || imsg.Channel != raceChannel)
return Task.CompletedTask;
return;
messagesSinceGameStarted++;
return Task.CompletedTask;
return;
}
private async Task CheckForFullGameAsync(CancellationToken cancelToken)
@ -222,28 +233,29 @@ namespace NadekoBot.Modules.Gambling
var 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;
}
var p = new Participant(u, animal, amount);
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;
}
if (Started)
{
await raceChannel.SendMessageAsync($"{u.Mention} `Race is already started`");
await raceChannel.SendErrorAsync($"{u.Mention} `Race is already started`").ConfigureAwait(false);
return;
}
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;
}
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;
}
public override int GetHashCode()
{
return User.GetHashCode();
}
public override int GetHashCode() => User.GetHashCode();
public override bool Equals(object obj)
{

View File

@ -19,6 +19,9 @@ namespace NadekoBot.Modules.Gambling
public class DriceRollCommands
{
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]
[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);
}
//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;
var rng = new NadekoRandom();
Match match;
if ((match = dndRegex.Match(arg)).Length != 0)
public enum RollOrderType
{
int n1;
int n2;
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);
}
}
Ordered,
Unordered
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[Priority(0)]
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;
if (channel == null)
return;
var ordered = true;
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;
}
@ -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);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Rolluo(IUserMessage umsg, string arg)
private async Task InternallDndRoll(IUserMessage umsg, string arg, bool ordered)
{
var channel = (ITextChannel)umsg.Channel;
if (channel == null)
return;
var ordered = false;
var rng = new NadekoRandom();
Match match;
if ((match = dndRegex.Match(arg)).Length != 0)
{
int n1;
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) &&
int.TryParse(match.Groups["n2"].ToString(), out n2) &&
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;
}
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]
[RequireContext(ContextType.Guild)]
public async Task Rolluo(IUserMessage umsg, int num)
{
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;
var embed = new EmbedBuilder().WithOkColor().WithDescription($"{umsg.Author.Mention} rolled {n1} {(n1 == 1 ? "die" : "dice")} `1 to {n2}` +`{add}` -`{sub}`")
.AddField(efb => efb.WithName(Format.Bold("Result"))
.WithValue(string.Join(" ", (ordered ? arr.OrderBy(x => x).AsEnumerable() : arr).Select(x => Format.Code(x.ToString())))));
await channel.EmbedAsync(embed.Build()).ConfigureAwait(false);
}
}
}
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]
[RequireContext(ContextType.Guild)]
@ -241,7 +206,7 @@ namespace NadekoBot.Modules.Gambling
.Select(int.Parse)
.ToArray();
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);
}
else
@ -249,11 +214,11 @@ namespace NadekoBot.Modules.Gambling
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)
{
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)
{
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;
}
var currentCard = cards.DrawACard();
@ -53,7 +53,7 @@ namespace NadekoBot.Modules.Gambling
MemoryStream bitmapStream = new MemoryStream();
images.Merge().SaveAsPng(bitmapStream);
bitmapStream.Position = 0;
//todo CARD NAMES?
var toSend = $"{msg.Author.Mention}";
if (cardObjects.Count == 5)
toSend += $" drew `{Cards.GetHandValue(cardObjects)}`";
@ -75,7 +75,7 @@ namespace NadekoBot.Modules.Gambling
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]
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 tailsPath = "data/images/coins/tails.png";
public FlipCoinCommands() { }
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
@ -28,14 +27,14 @@ namespace NadekoBot.Modules.Gambling
if (count == 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
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;
}
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;
}
var imgs = new Image[count];
@ -60,24 +59,16 @@ namespace NadekoBot.Modules.Gambling
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);
return;
}
// todo update this
long userFlowers;
using (var uow = DbHandler.UnitOfWork())
var removed = await CurrencyHandler.RemoveCurrencyAsync(guildUser, "Betflip Gamble", amount, false).ConfigureAwait(false);
if (!removed)
{
userFlowers = uow.Currency.GetOrCreate(umsg.Author.Id).Amount;
}
if (userFlowers < amount)
{
await channel.SendMessageAsync($"{umsg.Author.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}.").ConfigureAwait(false);
return;
}
await CurrencyHandler.RemoveCurrencyAsync(guildUser, "Betflip Gamble", amount, false).ConfigureAwait(false);
//heads = true
//tails = false

View File

@ -20,7 +20,7 @@ namespace NadekoBot.Modules.Gambling
public static string CurrencyPluralName { 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())
{
@ -51,7 +51,7 @@ namespace NadekoBot.Modules.Gambling
var members = role.Members().Where(u => u.Status != UserStatus.Offline && u.Status != UserStatus.Unknown);
var membersArray = members as IUser[] ?? members.ToArray();
var usr = membersArray[new NadekoRandom().Next(0, membersArray.Length)];
await 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]
@ -62,7 +62,7 @@ namespace NadekoBot.Modules.Gambling
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]
@ -71,7 +71,7 @@ namespace NadekoBot.Modules.Gambling
{
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]
@ -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);
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;
}
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]
@ -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 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]
@ -129,7 +129,7 @@ namespace NadekoBot.Modules.Gambling
amount)))
.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);
}
@ -144,9 +144,9 @@ namespace NadekoBot.Modules.Gambling
return;
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
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;
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
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]
@ -184,7 +184,7 @@ namespace NadekoBot.Modules.Gambling
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;
}
@ -212,7 +212,7 @@ namespace NadekoBot.Modules.Gambling
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]
@ -221,7 +221,7 @@ namespace NadekoBot.Modules.Gambling
{
var channel = (ITextChannel)umsg.Channel;
IEnumerable<Currency> richest;
IEnumerable<Currency> richest = new List<Currency>();
using (var uow = DbHandler.UnitOfWork())
{
richest = uow.Currency.GetTopRichest(10);
@ -235,7 +235,7 @@ $@"```xl
Id $$$
"),
(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);
}
}

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.Commands;
using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Services;
using NLog;
using Services.CleverBotApi;
@ -34,7 +35,7 @@ namespace NadekoBot.Modules.Games
{
var bot = ChatterBotFactory.Create(ChatterBotType.CLEVERBOT);
CleverbotGuilds = new ConcurrentDictionary<ulong, ChatterBotSession>(
uow.GuildConfigs.GetAll()
NadekoBot.AllGuildConfigs
.Where(gc => gc.CleverbotEnabled)
.ToDictionary(gc => gc.GuildId, gc => bot.CreateSession()));
}
@ -72,12 +73,11 @@ namespace NadekoBot.Modules.Games
var response = await cleverbot.Think(message).ConfigureAwait(false);
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.SendMessageAsync(msg.Author.Mention+" "+response).ConfigureAwait(false); // try twice :\
await msg.Channel.SendConfirmAsync(response.SanitizeMentions()).ConfigureAwait(false); // try twice :\
}
return true;
}
@ -97,7 +97,7 @@ namespace NadekoBot.Modules.Games
uow.GuildConfigs.SetCleverbotEnabled(channel.Guild.Id, 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;
}
@ -112,7 +112,7 @@ namespace NadekoBot.Modules.Games
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();
if (string.IsNullOrWhiteSpace(text))
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.Commands;
using Discord.WebSocket;
using Microsoft.EntityFrameworkCore;
using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Services;
@ -16,8 +17,6 @@ using System.Threading.Tasks;
namespace NadekoBot.Modules.Games
{
//todo make currency generation change and cooldown modifyable
//only by bot owner through commands
public partial class Games
{
/// <summary>
@ -30,51 +29,51 @@ namespace NadekoBot.Modules.Games
[Group]
public class PlantPickCommands
{
private Random rng;
private ConcurrentHashSet<ulong> generationChannels = new ConcurrentHashSet<ulong>();
private static ConcurrentHashSet<ulong> generationChannels { get; } = new ConcurrentHashSet<ulong>();
//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
private ConcurrentDictionary<ulong, DateTime> lastGenerations = new ConcurrentDictionary<ulong, DateTime>();
private static ConcurrentDictionary<ulong, DateTime> lastGenerations { get; } = new ConcurrentDictionary<ulong, DateTime>();
private float chance;
private int cooldown;
private Logger _log { get; }
private static ConcurrentHashSet<ulong> usersRecentlyPicked { get; } = new ConcurrentHashSet<ulong>();
public PlantPickCommands()
private static float chance { get; }
private static int cooldown { get; }
private static Logger _log { get; }
static PlantPickCommands()
{
_log = LogManager.GetCurrentClassLogger();
NadekoBot.Client.MessageReceived += PotentialFlowerGeneration;
rng = new NadekoRandom();
using (var uow = DbHandler.UnitOfWork())
{
var conf = uow.BotConfig.GetOrCreate();
var x =
generationChannels = new ConcurrentHashSet<ulong>(NadekoBot.AllGuildConfigs
.SelectMany(c => c.GenerateCurrencyChannelIds.Select(obj=>obj.ChannelId)));
.SelectMany(c => c.GenerateCurrencyChannelIds.Select(obj => obj.ChannelId)));
chance = conf.CurrencyGenerationChance;
cooldown = conf.CurrencyGenerationCooldown;
}
}
private Task PotentialFlowerGeneration(IMessage imsg)
private static async void PotentialFlowerGeneration(IMessage imsg)
{
try
{
var msg = imsg as IUserMessage;
if (msg == null || msg.IsAuthor() || msg.Author.IsBot)
return Task.CompletedTask;
return;
var channel = imsg.Channel as ITextChannel;
if (channel == null)
return Task.CompletedTask;
return;
if (!generationChannels.Contains(channel.Id))
return Task.CompletedTask;
return;
var t = Task.Run(async () =>
{
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
return;
@ -84,31 +83,30 @@ namespace NadekoBot.Modules.Games
if (num > 100)
{
lastGenerations.AddOrUpdate(channel.Id, DateTime.Now, (id, old) => DateTime.Now);
try
{
var sent = await channel.SendFileAsync(
GetRandomCurrencyImagePath(),
$"❗ A random { Gambling.Gambling.CurrencyName } appeared! Pick it up by typing `{NadekoBot.ModulePrefixes[typeof(Games).Name]}pick`")
.ConfigureAwait(false);
plantedFlowers.AddOrUpdate(channel.Id, new List<IUserMessage>() { sent }, (id, old) => { old.Add(sent); return old; });
}
catch { }
}
});
return Task.CompletedTask;
}
catch { }
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Pick(IUserMessage imsg)
{
var channel = (ITextChannel)imsg.Channel;
if (!channel.Guild.GetCurrentUser().GetPermissions(channel).ManageMessages)
{
await channel.SendMessageAsync("`I need manage channel permissions in order to process this command.`").ConfigureAwait(false);
if (!channel.Guild.GetCurrentUser().GetPermissions(channel).ManageMessages || !usersRecentlyPicked.Add(imsg.Author.Id))
return;
}
try
{
List<IUserMessage> msgs;
@ -119,12 +117,14 @@ namespace NadekoBot.Modules.Games
await Task.WhenAll(msgs.Select(toDelete => toDelete.DeleteAsync())).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 t = Task.Run(async () =>
var msg = await channel.SendConfirmAsync($"**{imsg.Author}** picked {msgs.Count}{Gambling.Gambling.CurrencySign}!").ConfigureAwait(false);
msg.DeleteAfter(10);
}
finally
{
await Task.Delay(10000).ConfigureAwait(false);
try { await msg.DeleteAsync().ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
});
await Task.Delay(60000);
usersRecentlyPicked.TryRemove(imsg.Author.Id);
}
}
[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);
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;
}
@ -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";
if (file == null)
{
msg = await channel.SendMessageAsync(Gambling.Gambling.CurrencySign).ConfigureAwait(false);
msg = await channel.SendConfirmAsync(Gambling.Gambling.CurrencySign).ConfigureAwait(false);
}
else
{
@ -166,7 +166,7 @@ namespace NadekoBot.Modules.Games
bool enabled;
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 };
if (!guildConfig.GenerateCurrencyChannelIds.Contains(toAdd))
@ -185,16 +185,19 @@ namespace NadekoBot.Modules.Games
}
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
{
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() =>
Directory.GetFiles("data/currency_images").OrderBy(s => rng.Next()).FirstOrDefault();
private static string GetRandomCurrencyImagePath()
{
var rng = new NadekoRandom();
return Directory.GetFiles("data/currency_images").OrderBy(s => rng.Next()).FirstOrDefault();
}
int GetRandomNumber()
{

View File

@ -1,6 +1,7 @@
using Discord;
using Discord.Commands;
using NadekoBot.Attributes;
using NadekoBot.Extensions;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
@ -44,7 +45,7 @@ namespace NadekoBot.Modules.Games
await poll.StartPoll().ConfigureAwait(false);
}
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]
@ -91,7 +92,7 @@ namespace NadekoBot.Modules.Games
msgToSend += "\n**Private Message me with the corresponding number of the answer.**";
else
msgToSend += "\n**Send a Message here with the corresponding number of the answer.**";
await originalMessage.Channel.SendMessageAsync(msgToSend).ConfigureAwait(false);
await originalMessage.Channel.SendConfirmAsync(msgToSend).ConfigureAwait(false);
}
public async Task StopPoll()
@ -115,7 +116,7 @@ namespace NadekoBot.Modules.Games
$" has {kvp.Value} votes." +
$"({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)
{
@ -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
var msg = imsg as IUserMessage;
if (msg == null || msg.Author.IsBot)
return Task.CompletedTask;
return;
// has to be an integer
int vote;
if (!int.TryParse(imsg.Content, out vote))
return Task.CompletedTask;
return;
if (vote < 1 || vote > answers.Length)
return Task.CompletedTask;
return;
var t = Task.Run(async () =>
{
try
{
IMessageChannel ch;
if (isPublic)
{
@ -166,19 +165,16 @@ namespace NadekoBot.Modules.Games
{
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
{
var toDelete = await ch.SendMessageAsync($"{msg.Author.Mention} cast their vote.").ConfigureAwait(false);
await Task.Delay(5000);
await toDelete.DeleteAsync().ConfigureAwait(false);
var toDelete = await ch.SendConfirmAsync($"{msg.Author.Mention} cast their vote.").ConfigureAwait(false);
toDelete.DeleteAfter(5);
}
}
}
catch { }
});
return Task.CompletedTask;
}
}
}

View File

@ -21,9 +21,9 @@ namespace NadekoBot.Modules.Games
public class TypingGame
{
public const float WORD_VALUE = 4.5f;
private readonly ITextChannel channel;
public string CurrentSentence;
public bool IsActive;
public ITextChannel Channel { get; }
public string CurrentSentence { get; private set; }
public bool IsActive { get; private set; }
private readonly Stopwatch sw;
private readonly List<ulong> finishedUserIds;
private Logger _log { get; }
@ -31,14 +31,12 @@ namespace NadekoBot.Modules.Games
public TypingGame(ITextChannel channel)
{
_log = LogManager.GetCurrentClassLogger();
this.channel = channel;
this.Channel = channel;
IsActive = false;
sw = new Stopwatch();
finishedUserIds = new List<ulong>();
}
public ITextChannel Channel { get; set; }
public async Task<bool> Stop()
{
if (!IsActive) return false;
@ -47,7 +45,7 @@ namespace NadekoBot.Modules.Games
IsActive = false;
sw.Stop();
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;
}
@ -59,10 +57,10 @@ namespace NadekoBot.Modules.Games
var i = (int)(CurrentSentence.Length / WORD_VALUE * 1.7f);
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);
try
{
@ -107,18 +105,17 @@ namespace NadekoBot.Modules.Games
NadekoBot.Client.MessageReceived += AnswerReceived;
}
private Task 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 () =>
private async void AnswerReceived(IMessage imsg)
{
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;
@ -126,17 +123,21 @@ namespace NadekoBot.Modules.Games
var decision = Judge(distance, guess.Length);
if (decision && !finishedUserIds.Contains(msg.Author.Id))
{
var wpm = CurrentSentence.Length / WORD_VALUE / sw.Elapsed.Seconds * 60;
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)
{
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 { }
});
return Task.CompletedTask;
catch (Exception ex) { _log.Warn(ex); }
}
private bool Judge(int errors, int textLength) => errors <= textLength / 25;
@ -172,7 +173,7 @@ namespace NadekoBot.Modules.Games
if (game.IsActive)
{
await channel.SendMessageAsync(
await channel.SendErrorAsync(
$"Contest already running in " +
$"{game.Channel.Mention} channel.")
.ConfigureAwait(false);
@ -194,7 +195,7 @@ namespace NadekoBot.Modules.Games
await game.Stop().ConfigureAwait(false);
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));
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]
@ -229,11 +230,11 @@ namespace NadekoBot.Modules.Games
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;
}
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);
}
@ -253,7 +254,7 @@ namespace NadekoBot.Modules.Games
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);
}
}

View File

@ -22,7 +22,7 @@ namespace NadekoBot.Modules.Games.Trivia
private int QuestionDurationMiliseconds { get; } = 30000;
private int HintTimeoutMiliseconds { get; } = 6000;
public bool ShowHints { get; set; } = true;
public bool ShowHints { get; } = true;
private CancellationTokenSource triviaCancelSource { get; set; }
public TriviaQuestion CurrentQuestion { get; private set; }
@ -35,52 +35,71 @@ namespace NadekoBot.Modules.Games.Trivia
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();
ShowHints = showHints;
this._log = LogManager.GetCurrentClassLogger();
this.ShowHints = showHints;
this.guild = guild;
this.channel = channel;
WinRequirement = winReq;
Task.Run(async () => { try { await StartGame().ConfigureAwait(false); } catch { } });
this.WinRequirement = winReq;
}
private async Task StartGame()
public async Task StartGame()
{
while (!ShouldStopGame)
{
// reset the cancellation source
triviaCancelSource = new CancellationTokenSource();
var token = triviaCancelSource.Token;
// load question
CurrentQuestion = TriviaQuestionPool.Instance.GetRandomQuestion(oldQuestions);
if (CurrentQuestion == null)
{
try { await channel.SendMessageAsync($":exclamation: Failed loading a trivia question").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
await End().ConfigureAwait(false);
await channel.SendErrorAsync("Trivia Game", "Failed loading a question.").ConfigureAwait(false);
return;
}
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)
{
break;
return;
}
catch (Exception ex)
{
_log.Warn(ex);
await Task.Delay(2000).ConfigureAwait(false);
continue;
}
catch (Exception ex) { _log.Warn(ex); }
//receive messages
try
{
NadekoBot.Client.MessageReceived += PotentialGuess;
//allow people to guess
GameActive = true;
try
{
//hint
await Task.Delay(HintTimeoutMiliseconds, token).ConfigureAwait(false);
await Task.Delay(HintTimeoutMiliseconds, triviaCancelSource.Token).ConfigureAwait(false);
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)
{
break;
@ -88,53 +107,57 @@ namespace NadekoBot.Modules.Games.Trivia
catch (Exception ex) { _log.Warn(ex); }
//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
}
finally
{
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;
// 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);
}
try { NadekoBot.Client.MessageReceived -= PotentialGuess; } catch { }
GameActive = false;
await End().ConfigureAwait(false);
}
private async Task End()
public async Task EnsureStopped()
{
ShouldStopGame = true;
TriviaGame throwaway;
Games.TriviaCommands.RunningTrivias.TryRemove(channel.Guild.Id, out throwaway);
try { await channel.SendMessageAsync("**Trivia game ended**\n" + GetLeaderboard()).ConfigureAwait(false); } catch { }
await channel.EmbedAsync(new EmbedBuilder().WithOkColor()
.WithAuthor(eab => eab.WithName("Trivia Game Ended"))
.WithTitle("Final Results")
.WithDescription(GetLeaderboard())
.Build()).ConfigureAwait(false);
}
public async Task StopGame()
{
if (!ShouldStopGame)
try { await channel.SendMessageAsync(":exclamation: Trivia will stop after this question.").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
var old = ShouldStopGame;
ShouldStopGame = true;
if (!old)
try { await channel.SendConfirmAsync("Trivia Game", "Stopping after this question.").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
}
private Task 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 () =>
private async void PotentialGuess(IMessage imsg)
{
try
{
if (!(umsg.Channel is IGuildChannel && umsg.Channel is ITextChannel)) return;
if ((umsg.Channel as ITextChannel).Guild != guild) return;
if (umsg.Author.Id == NadekoBot.Client.GetCurrentUser().Id) return;
if (imsg.Author.IsBot)
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;
await _guessLock.WaitAsync().ConfigureAwait(false);
@ -149,23 +172,26 @@ namespace NadekoBot.Modules.Games.Trivia
finally { _guessLock.Release(); }
if (!guess) return;
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;
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); }
});
return Task.CompletedTask;
}
public string GetLeaderboard()
{
if (Users.Count == 0)
return "";
return "No results.";
var sb = new StringBuilder();
sb.Append("**Leaderboard:**\n-----------\n");
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 string Category;
public string Question;
public string Answer;
public string Category { get; set; }
public string Question { get; set; }
public string Answer { get; set; }
public TriviaQuestion(string q, string a, string c)
{
@ -79,12 +79,9 @@ namespace NadekoBot.Modules.Games.Trivia
return str;
}
public override string ToString() =>
"Question: **" + this.Question + "?**";
private static string Scramble(string word)
{
var letters = word.ToArray();
var letters = word.ToCharArray();
var count = 0;
for (var i = 0; i < letters.Length; i++)
{
@ -101,7 +98,7 @@ namespace NadekoBot.Modules.Games.Trivia
if (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.Services;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Concurrent;
@ -11,36 +12,31 @@ namespace NadekoBot.Modules.Games.Trivia
{
public class TriviaQuestionPool
{
public static TriviaQuestionPool Instance { get; } = new TriviaQuestionPool();
public ConcurrentHashSet<TriviaQuestion> pool = new ConcurrentHashSet<TriviaQuestion>();
private static TriviaQuestionPool _instance;
public static TriviaQuestionPool Instance { get; } = _instance ?? (_instance = new TriviaQuestionPool());
private const string questionsFile = "data/trivia_questions.json";
private Random rng { get; } = new NadekoRandom();
private TriviaQuestion[] pool { get; }
static 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();
var rand = rng.Next(0, list.Count);
return list[rand];
}
if (pool.Length == 0)
return null;
public void Reload()
{
var arr = JArray.Parse(File.ReadAllText("data/questions.json"));
TriviaQuestion randomQuestion;
while (exclude.Contains(randomQuestion = pool[rng.Next(0, pool.Length)])) ;
foreach (var item in arr)
{
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()));
return randomQuestion;
}
}
}

View File

@ -1,6 +1,7 @@
using Discord;
using Discord.Commands;
using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Modules.Games.Trivia;
using System;
using System.Collections.Concurrent;
@ -19,29 +20,33 @@ namespace NadekoBot.Modules.Games
[NadekoCommand, Usage, Description, Aliases]
[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;
TriviaGame trivia;
if (!RunningTrivias.TryGetValue(channel.Guild.Id, out trivia))
var showHints = !additionalArgs.Contains("nohint");
TriviaGame trivia = new TriviaGame(channel.Guild, channel, showHints, winReq);
if (RunningTrivias.TryAdd(channel.Guild.Id, trivia))
{
var showHints = !args.Contains("nohint");
var number = args.Select(s =>
try
{
int num;
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);
await trivia.StartGame().ConfigureAwait(false);
}
else
await channel.SendMessageAsync("Trivia game is already running on this server.\n" + trivia.CurrentQuestion).ConfigureAwait(false);
finally
{
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]
@ -52,9 +57,12 @@ namespace NadekoBot.Modules.Games
TriviaGame trivia;
if (RunningTrivias.TryGetValue(channel.Guild.Id, out trivia))
await channel.SendMessageAsync(trivia.GetLeaderboard()).ConfigureAwait(false);
else
await channel.SendMessageAsync("No trivia is running on this server.").ConfigureAwait(false);
{
await channel.SendConfirmAsync("Leaderboard", trivia.GetLeaderboard()).ConfigureAwait(false);
return;
}
await channel.SendErrorAsync("No trivia is running on this server.").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
@ -64,12 +72,13 @@ namespace NadekoBot.Modules.Games
var channel = (ITextChannel)umsg.Channel;
TriviaGame trivia;
if (RunningTrivias.TryRemove(channel.Guild.Id, out trivia))
if (RunningTrivias.TryGetValue(channel.Guild.Id, out trivia))
{
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)
return;
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]
@ -48,8 +48,11 @@ namespace NadekoBot.Modules.Games
if (string.IsNullOrWhiteSpace(question))
return;
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]
@ -99,7 +102,7 @@ namespace NadekoBot.Modules.Games
else
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]
@ -108,7 +111,7 @@ namespace NadekoBot.Modules.Games
{
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.
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)
{
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`")
.ConfigureAwait(false);
var embed = new EmbedBuilder().WithOkColor().WithFooter(efb => efb.WithText($" Type `-cmds ModuleName` to get a list of commands in that module. eg `-cmds games`"))
.WithTitle("📜 List Of Modules").WithDescription("\n• " + string.Join("\n• ", NadekoBot.CommandService.Modules.Select(m => m.Name).OrderBy(s=>s)))
.Build();
await umsg.Channel.EmbedAsync(embed).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
@ -50,7 +52,7 @@ namespace NadekoBot.Modules.Help
module = module?.Trim().ToUpperInvariant();
if (string.IsNullOrWhiteSpace(module))
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)
.Distinct(new CommandTextEqualityComparer())
.AsEnumerable();
@ -58,7 +60,7 @@ namespace NadekoBot.Modules.Help
var cmdsArray = cmds as Command[] ?? cmds.ToArray();
if (!cmdsArray.Any())
{
await channel.SendMessageAsync("🚫 **That module does not exist.**").ConfigureAwait(false);
await channel.SendErrorAsync("That module does not exist.").ConfigureAwait(false);
return;
}
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($" **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]
@ -84,20 +86,22 @@ namespace NadekoBot.Modules.Help
await ch.SendMessageAsync(HelpString).ConfigureAwait(false);
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)
{
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;
}
var str = $"**__Help for:__ `{com.Text}`**";
var str = $"**`{com.Text}`**";
var alias = com.Aliases.Skip(1).FirstOrDefault();
if (alias != null)
str += $" / `{alias}`";
if (com != null)
await channel.SendMessageAsync(str + $@"{Environment.NewLine}**Desc:** {string.Format(com.Summary, com.Module.Prefix)} {GetCommandRequirements(com)}
**Usage:** {string.Format(com.Remarks, com.Module.Prefix)}").ConfigureAwait(false);
str += $" **/ `{alias}`**";
var embed = new EmbedBuilder()
.AddField(fb => fb.WithIndex(1).WithName(str).WithValue($"{ string.Format(com.Summary, com.Module.Prefix)} { GetCommandRequirements(com)}").WithIsInline(true))
.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)
@ -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 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)
{
@ -154,7 +158,7 @@ namespace NadekoBot.Modules.Help
{
var channel = (ITextChannel)umsg.Channel;
await channel.SendMessageAsync(
await channel.SendConfirmAsync(
@"**LIST OF COMMANDS**: <http://nadekobot.readthedocs.io/en/latest/Commands%20List/>
**Hosting Guides and docs can be found here**: <http://nadekobot.readthedocs.io/en/latest/>").ConfigureAwait(false);
}
@ -165,7 +169,7 @@ namespace NadekoBot.Modules.Help
{
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 send donations to `nadekodiscordbot@gmail.com`
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; }
/// <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>();
public IReadOnlyCollection<Song> Playlist => playlist;
@ -41,8 +61,9 @@ namespace NadekoBot.Modules.Music.Classes
public float Volume { get; private set; }
public event EventHandler<Song> OnCompleted = delegate { };
public event EventHandler<Song> OnStarted = delegate { };
public event Action<MusicPlayer, Song> OnCompleted = delegate { };
public event Action<MusicPlayer, Song> OnStarted = delegate { };
public event Action<bool> OnPauseChanged = delegate { };
public IVoiceChannel PlaybackVoiceChannel { get; private set; }
@ -52,7 +73,9 @@ namespace NadekoBot.Modules.Music.Classes
public bool Autoplay { get; set; } = false;
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)
{
@ -106,11 +129,13 @@ namespace NadekoBot.Modules.Music.Classes
}
CurrentSong = GetNextSong();
RemoveSongAt(0);
if (CurrentSong == null)
continue;
var index = playlist.IndexOf(CurrentSong);
if (index != -1)
RemoveSongAt(index);
OnStarted(this, CurrentSong);
await CurrentSong.Play(audioClient, cancelToken);
@ -129,6 +154,7 @@ namespace NadekoBot.Modules.Music.Classes
{
Console.WriteLine("Music thread almost crashed.");
Console.WriteLine(ex);
await Task.Delay(3000).ConfigureAwait(false);
}
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)
{
@ -181,8 +207,26 @@ namespace NadekoBot.Modules.Music.Classes
return volume;
}
private Song GetNextSong() =>
playlist.FirstOrDefault();
private Song GetNextSong()
{
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()
{
@ -249,7 +293,7 @@ namespace NadekoBot.Modules.Music.Classes
{
var curSong = CurrentSong;
var toUpdate = playlist.Where(s => s.SongInfo.ProviderType == MusicType.Normal &&
s.TotalLength == TimeSpan.Zero);
s.TotalTime == TimeSpan.Zero);
if (curSong != null)
toUpdate = toUpdate.Append(curSong);
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))
{
s.TotalLength = kvp.Value;
s.TotalTime = kvp.Value;
return;
}
}
});
}
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.Tasks;
using VideoLibrary;
using System.Net;
namespace NadekoBot.Modules.Music.Classes
{
@ -18,35 +19,20 @@ namespace NadekoBot.Modules.Music.Classes
{
public string Provider { 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 Title { get; set; }
public string Uri { get; set; }
public string AlbumArt { get; set; }
}
public class Song
{
public StreamState State { get; set; }
public string PrettyName =>
$"**【 {SongInfo.Title.TrimTo(55)} 】**`{(SongInfo.Provider ?? "-")}` `by {QueuerName}`";
public SongInfo SongInfo { get; }
public MusicPlayer MusicPlayer { get; set; }
public string QueuerName { get; set; }
public MusicPlayer MusicPlayer { get; set; }
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;
}
public TimeSpan TotalTime { get; set; } = TimeSpan.Zero;
public TimeSpan CurrentTime => TimeSpan.FromSeconds(bytesSent / frameBytes / (1000 / milliseconds));
const int milliseconds = 20;
const int samplesPerFrame = (48000 / 1000) * milliseconds;
@ -54,11 +40,71 @@ namespace NadekoBot.Modules.Music.Classes
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 Logger _log;
public int SkipTo {
get { return skipTo; }
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)
{
@ -79,16 +125,10 @@ namespace NadekoBot.Modules.Music.Classes
{
var s = new Song(SongInfo);
s.MusicPlayer = MusicPlayer;
s.State = StreamState.Queued;
s.QueuerName = QueuerName;
return s;
}
public Song SetMusicPlayer(MusicPlayer mp)
{
this.MusicPlayer = mp;
return this;
}
public async Task Play(IAudioClient voiceClient, CancellationToken cancelToken)
{
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);
var bufferTask = inStream.BufferSong(cancelToken).ConfigureAwait(false);
bytesSent = 0;
try
{
var attempt = 0;
@ -140,19 +178,20 @@ namespace NadekoBot.Modules.Music.Classes
finished = true;
}
sw.Stop();
_log.Debug("Prebuffering successfully completed in "+ sw.Elapsed);
_log.Debug("Prebuffering successfully completed in " + sw.Elapsed);
var outStream = voiceClient.CreatePCMStream(960);
int nextTime = Environment.TickCount + milliseconds;
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---------");
var read = await inStream.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false);
//await inStream.CopyToAsync(voiceClient.OutputStream);
if(read < frameBytes)
if (read < frameBytes)
_log.Debug("read {0}", read);
unchecked
{
@ -172,10 +211,15 @@ namespace NadekoBot.Modules.Music.Classes
if (slowconnection)
{
_log.Warn("Slow connection has disrupted music, waiting a bit for buffer");
await Task.Delay(1000, cancelToken).ConfigureAwait(false);
nextTime = Environment.TickCount + milliseconds;
}
else
{
await Task.Delay(100, cancelToken).ConfigureAwait(false);
nextTime = Environment.TickCount + milliseconds;
}
}
else
attempt = 0;
@ -184,7 +228,10 @@ namespace NadekoBot.Modules.Music.Classes
attempt = 0;
while (this.MusicPlayer.Paused)
{
await Task.Delay(200, cancelToken).ConfigureAwait(false);
nextTime = Environment.TickCount + milliseconds;
}
buffer = AdjustVolume(buffer, MusicPlayer.Volume);
@ -199,7 +246,7 @@ namespace NadekoBot.Modules.Music.Classes
finally
{
await bufferTask;
if(inStream != null)
if (inStream != null)
inStream.Dispose();
}
}
@ -213,35 +260,6 @@ namespace NadekoBot.Modules.Music.Classes
_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
public unsafe static byte[] AdjustVolume(byte[] audioSamples, float volume)
{
@ -267,200 +285,5 @@ namespace NadekoBot.Modules.Music.Classes
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,21 +25,21 @@ namespace NadekoBot.Modules.Music.Classes
_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 NextFileToRead = 0;
public bool BufferingCompleted { get; private set;} = false;
public bool BufferingCompleted { get; private set; } = false;
private ulong CurrentBufferSize = 0;
@ -76,7 +76,8 @@ namespace NadekoBot.Modules.Music.Classes
try
{
outStream.Dispose();
}catch { }
}
catch { }
outStream = new FileStream(Basename + "-" + ++FileNumber, FileMode.Append, FileAccess.Write, FileShare.Read);
currentFileSize = bytesRead;
}
@ -108,7 +109,7 @@ Check the guides for your platform on how to setup ffmpeg correctly:
}
finally
{
if(outStream != null)
if (outStream != null)
outStream.Dispose();
Console.WriteLine($"Buffering done.");
if (p != null)
@ -151,7 +152,7 @@ Check the guides for your platform on how to setup ffmpeg correctly:
private void CleanFiles()
{
for (long i = NextFileToRead - 1 ; i <= FileNumber; i++)
for (long i = NextFileToRead - 1; i <= FileNumber; i++)
{
try
{
@ -169,7 +170,7 @@ Check the guides for your platform on how to setup ffmpeg correctly:
public override bool CanWrite => false;
public override long Length => (long) CurrentBufferSize;
public override long Length => (long)CurrentBufferSize;
public override long Position { get; set; } = 0;
@ -178,7 +179,7 @@ Check the guides for your platform on how to setup ffmpeg correctly:
public override int Read(byte[] buffer, int offset, int count)
{
int read = CurrentFileStream.Read(buffer, offset, count);
if(read < count)
if (read < count)
{
if (!BufferingCompleted || IsNextFileReady())
{

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; }
[JsonProperty("permalink_url")]
public string TrackLink { get; set; } = "";
public string artwork_url { get; set; } = "";
[JsonIgnore]
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 System.Collections.Generic;
using NadekoBot.Services.Database.Models;
using System.Text.RegularExpressions;
using System.Threading;
namespace NadekoBot.Modules.Music
{
[NadekoModule("Music", "!!", AutoLoad = false)]
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";
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
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]
@ -75,33 +97,45 @@ namespace NadekoBot.Modules.Music
[NadekoCommand, Usage, Description, Aliases]
[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;
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();
if (((IGuildUser)umsg.Author).VoiceChannel != musicPlayer.PlaybackVoiceChannel)
return Task.CompletedTask;
musicPlayer.TogglePause();
return Task.CompletedTask;
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Pause(IUserMessage umsg)
public async Task Fairplay(IUserMessage umsg)
{
var channel = (ITextChannel)umsg.Channel;
MusicPlayer musicPlayer;
if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer)) return;
if (((IGuildUser)umsg.Author).VoiceChannel != musicPlayer.PlaybackVoiceChannel)
return;
musicPlayer.TogglePause();
if (musicPlayer.Paused)
await channel.SendMessageAsync("🎵`Music Player paused.`").ConfigureAwait(false);
else
await channel.SendMessageAsync("🎵`Music Player unpaused.`").ConfigureAwait(false);
var val = musicPlayer.FairPlay = !musicPlayer.FairPlay;
await channel.SendConfirmAsync("Fair play " + (val ? "enabled" : "disabled") + ".").ConfigureAwait(false);
}
[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);
if (channel.Guild.GetCurrentUser().GetPermissions(channel).ManageMessages)
{
await Task.Delay(10000).ConfigureAwait(false);
await ((IUserMessage)umsg).DeleteAsync().ConfigureAwait(false);
umsg.DeleteAfter(10);
}
}
@ -127,8 +160,7 @@ namespace NadekoBot.Modules.Music
await QueueSong(((IGuildUser)umsg.Author), channel, ((IGuildUser)umsg.Author).VoiceChannel, query, musicType: MusicType.Soundcloud).ConfigureAwait(false);
if (channel.Guild.GetCurrentUser().GetPermissions(channel).ManageMessages)
{
await Task.Delay(10000).ConfigureAwait(false);
await ((IUserMessage)umsg).DeleteAsync().ConfigureAwait(false);
umsg.DeleteAfter(10);
}
}
@ -140,7 +172,7 @@ namespace NadekoBot.Modules.Music
MusicPlayer 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;
}
if (page <= 0)
@ -148,27 +180,44 @@ namespace NadekoBot.Modules.Music
var currentSong = musicPlayer.CurrentSong;
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";
if (musicPlayer.RepeatSong)
toSend += "🔂";
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;
try { await musicPlayer.UpdateSongDurationsAsync().ConfigureAwait(false); } catch { }
const int itemsPerPage = 10;
int startAt = itemsPerPage * (page - 1);
var number = 1 + startAt;
await channel.SendMessageAsync(toSend + string.Join("\n", musicPlayer.Playlist.Skip(startAt).Take(15).Select(v => $"`{number++}.` {v.PrettyName}"))).ConfigureAwait(false);
var number = 0 + startAt;
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]
@ -176,19 +225,22 @@ namespace NadekoBot.Modules.Music
public async Task NowPlaying(IUserMessage umsg)
{
var channel = (ITextChannel)umsg.Channel;
MusicPlayer musicPlayer;
if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer))
return;
var currentSong = musicPlayer.CurrentSong;
if (currentSong == null)
return;
try { await musicPlayer.UpdateSongDurationsAsync().ConfigureAwait(false); } catch { }
if (currentSong.TotalLength == TimeSpan.Zero)
{
await musicPlayer.UpdateSongDurationsAsync().ConfigureAwait(false);
}
await channel.SendMessageAsync($"🎵`Now Playing` {currentSong.PrettyName} " +
$"{currentSong.PrettyCurrentTime()}").ConfigureAwait(false);
var embed = new EmbedBuilder().WithOkColor()
.WithAuthor(eab => eab.WithName("Now Playing").WithMusicIcon())
.WithDescription(currentSong.PrettyName)
.WithThumbnail(tn => tn.Url = currentSong.Thumbnail)
.WithFooter(ef => ef.WithText(musicPlayer.PrettyVolume + " | " + currentSong.PrettyFullTime + $" | {currentSong.PrettyProvider} | {currentSong.QueuerName}"));
await channel.EmbedAsync(embed.Build()).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
@ -204,7 +256,7 @@ namespace NadekoBot.Modules.Music
if (val < 0)
return;
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]
@ -215,15 +267,15 @@ namespace NadekoBot.Modules.Music
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;
}
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();
}
await channel.SendMessageAsync($"🎵 `Default volume set to {val}%`").ConfigureAwait(false);
await channel.SendConfirmAsync($"🎵 Default volume set to {val}%").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
@ -238,12 +290,12 @@ namespace NadekoBot.Modules.Music
return;
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;
}
musicPlayer.Shuffle();
await channel.SendMessageAsync("🎵 `Songs shuffled.`").ConfigureAwait(false);
await channel.SendConfirmAsync("🎵 Songs shuffled.").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
@ -256,35 +308,48 @@ namespace NadekoBot.Modules.Music
return;
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;
}
var plId = (await _google.GetPlaylistIdsByKeywordsAsync(arg).ConfigureAwait(false)).FirstOrDefault();
var plId = (await NadekoBot.Google.GetPlaylistIdsByKeywordsAsync(arg).ConfigureAwait(false)).FirstOrDefault();
if (plId == null)
{
await channel.SendMessageAsync("No search results for that query.");
await channel.SendErrorAsync("No search results for that query.");
return;
}
var ids = await _google.GetPlaylistTracksAsync(plId, 500).ConfigureAwait(false);
var ids = await NadekoBot.Google.GetPlaylistTracksAsync(plId, 500).ConfigureAwait(false);
if (!ids.Any())
{
await channel.SendMessageAsync($"🎵 `Failed to find any songs.`").ConfigureAwait(false);
await channel.SendErrorAsync($"🎵 Failed to find any songs.").ConfigureAwait(false);
return;
}
var idArray = ids as string[] ?? ids.ToArray();
var count = idArray.Length;
var msg =
await channel.SendMessageAsync($"🎵 `Attempting to queue {count} songs".SnPl(count) + "...`").ConfigureAwait(false);
foreach (var id in idArray)
var count = ids.Count();
var msg = await channel.SendMessageAsync($"🎵 Attempting to queue **{count}** songs".SnPl(count) + "...").ConfigureAwait(false);
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
{
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 { 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]
@ -338,11 +403,12 @@ namespace NadekoBot.Modules.Music
var dir = new DirectoryInfo(arg);
var fileEnum = dir.GetFiles("*", SearchOption.AllDirectories)
.Where(x => !x.Attributes.HasFlag(FileAttributes.Hidden | FileAttributes.System));
var gusr = (IGuildUser)umsg.Author;
foreach (var file in fileEnum)
{
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)
{
@ -350,7 +416,7 @@ namespace NadekoBot.Modules.Music
}
catch { }
}
await channel.SendMessageAsync("🎵 `Directory queue complete.`").ConfigureAwait(false);
await channel.SendConfirmAsync("🎵 Directory queue complete.").ConfigureAwait(false);
}
catch { }
}
@ -362,14 +428,13 @@ namespace NadekoBot.Modules.Music
var channel = (ITextChannel)umsg.Channel;
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;
}
await QueueSong(((IGuildUser)umsg.Author), channel, ((IGuildUser)umsg.Author).VoiceChannel, radio_link, musicType: MusicType.Radio).ConfigureAwait(false);
if (channel.Guild.GetCurrentUser().GetPermissions(channel).ManageMessages)
{
await Task.Delay(10000).ConfigureAwait(false);
await ((IUserMessage)umsg).DeleteAsync().ConfigureAwait(false);
umsg.DeleteAfter(10);
}
}
@ -416,7 +481,15 @@ namespace NadekoBot.Modules.Music
return;
var song = (musicPlayer.Playlist as List<Song>)?[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]
@ -431,7 +504,7 @@ namespace NadekoBot.Modules.Music
MusicPlayer musicPlayer;
if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer)) return;
musicPlayer.ClearQueue();
await channel.SendMessageAsync($"🎵`Queue cleared!`").ConfigureAwait(false);
await channel.SendConfirmAsync($"🎵 Queue cleared!").ConfigureAwait(false);
return;
}
@ -457,7 +530,7 @@ namespace NadekoBot.Modules.Music
!int.TryParse(fromtoArr[1], out n2) || n1 < 1 || n2 < 1 || n1 == n2 ||
n1 > playlist.Count || n2 > playlist.Count)
{
await channel.SendMessageAsync("`Invalid input.`").ConfigureAwait(false);
await channel.SendErrorAsync("Invalid input.").ConfigureAwait(false);
return;
}
@ -466,9 +539,15 @@ namespace NadekoBot.Modules.Music
var nn1 = n2 < n1 ? n1 : n1 - 1;
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]
@ -478,11 +557,27 @@ namespace NadekoBot.Modules.Music
var channel = (ITextChannel)umsg.Channel;
MusicPlayer musicPlayer;
if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer))
{
return;
}
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]
@ -497,9 +592,16 @@ namespace NadekoBot.Modules.Music
if (currentSong == null)
return;
var currentValue = musicPlayer.ToggleRepeatSong();
await channel.SendMessageAsync(currentValue ?
$"🎵🔂`Repeating track:`{currentSong.PrettyName}" :
$"🎵🔂`Current track repeat stopped.`")
if (currentValue)
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);
}
@ -512,7 +614,7 @@ namespace NadekoBot.Modules.Music
if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer))
return;
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]
@ -526,7 +628,8 @@ namespace NadekoBot.Modules.Music
var curSong = musicPlayer.CurrentSong;
var songs = musicPlayer.Playlist.Append(curSong)
.Select(s=> new PlaylistSong() {
.Select(s => new PlaylistSong()
{
Provider = s.SongInfo.Provider,
ProviderType = s.SongInfo.ProviderType,
Title = s.SongInfo.Title,
@ -548,7 +651,7 @@ namespace NadekoBot.Modules.Music
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]
@ -565,14 +668,15 @@ namespace NadekoBot.Modules.Music
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;
}
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)
{
var usr = (IGuildUser)umsg.Author;
try
{
await QueueSong(usr, channel, usr.VoiceChannel, item.Query, true, item.ProviderType).ConfigureAwait(false);
@ -581,7 +685,7 @@ namespace NadekoBot.Modules.Music
catch { break; }
}
if (msg != null)
await msg.ModifyAsync(m => m.Content = $"`Done loading playlist {mpl.Name}.`").ConfigureAwait(false);
await msg.ModifyAsync(m => m.Content = $"✅ Done loading playlist **{mpl.Name}**.").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
@ -600,9 +704,12 @@ namespace NadekoBot.Modules.Music
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
@ -634,9 +741,9 @@ namespace NadekoBot.Modules.Music
}
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
await channel.SendMessageAsync("`Playlist successfully deleted.`").ConfigureAwait(false);
await channel.SendConfirmAsync("🗑 Playlist successfully **deleted**.").ConfigureAwait(false);
}
catch (Exception ex)
{
@ -678,42 +785,7 @@ namespace NadekoBot.Modules.Music
if (seconds.Length == 1)
seconds = "0" + seconds;
await channel.SendMessageAsync($"`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);
}
await channel.SendConfirmAsync($"Skipped to `{minutes}:{seconds}`").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
@ -726,9 +798,9 @@ namespace NadekoBot.Modules.Music
return;
if (!musicPlayer.ToggleAutoplay())
await channel.SendMessageAsync("🎶`Autoplay disabled.`").ConfigureAwait(false);
await channel.SendConfirmAsync("❌ Autoplay disabled.").ConfigureAwait(false);
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)
@ -736,7 +808,7 @@ namespace NadekoBot.Modules.Music
if (voiceCh == null || voiceCh.Guild != textCh.Guild)
{
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));
}
if (string.IsNullOrWhiteSpace(query) || query.Length < 3)
@ -747,43 +819,72 @@ namespace NadekoBot.Modules.Music
float vol = 1;// SpecificConfigurations.Default.Of(server.Id).DefaultMusicVolume;
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);
IUserMessage playingMessage = null;
IUserMessage lastFinishedMessage = null;
IUserMessage finishedMessage = null;
mp.OnCompleted += async (s, song) =>
{
if (song.PrintStatusMessage)
{
try
{
if (lastFinishedMessage != null)
await lastFinishedMessage.DeleteAsync().ConfigureAwait(false);
if (playingMessage != null)
await playingMessage.DeleteAsync().ConfigureAwait(false);
try { lastFinishedMessage = await textCh.SendMessageAsync($"🎵`Finished`{song.PrettyName}").ConfigureAwait(false); } catch { }
if (finishedMessage != null)
finishedMessage.DeleteAfter(0);
finishedMessage = await textCh.EmbedAsync(new EmbedBuilder().WithOkColor()
.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")
{
await QueueSong(queuer.Guild.GetCurrentUser(), textCh, voiceCh, (await NadekoBot.Google.GetRelatedVideosAsync(song.SongInfo.Query, 4)).ToList().Shuffle().FirstOrDefault(), silent, musicType).ConfigureAwait(false);
}
}
catch { }
}
};
mp.OnStarted += async (s, song) =>
IUserMessage playingMessage = null;
mp.OnStarted += async (player, song) =>
{
if (song.PrintStatusMessage)
{
var sender = s as MusicPlayer;
try { await mp.UpdateSongDurationsAsync().ConfigureAwait(false); } catch { }
var sender = player as MusicPlayer;
if (sender == null)
return;
try
{
if (playingMessage != null)
playingMessage.DeleteAfter(0);
var msgTxt = $"🎵`Playing`{song.PrettyName} `Vol: {(int)(sender.Volume * 100)}%`";
try { playingMessage = await textCh.SendMessageAsync(msgTxt).ConfigureAwait(false); } catch { }
playingMessage = await textCh.EmbedAsync(new EmbedBuilder().WithOkColor()
.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;
});
@ -791,7 +892,7 @@ namespace NadekoBot.Modules.Music
try
{
musicPlayer.ThrowIfQueueFull();
resolvedSong = await Song.ResolveSong(query, musicType).ConfigureAwait(false);
resolvedSong = await SongHandler.ResolveSong(query, musicType).ConfigureAwait(false);
if (resolvedSong == null)
throw new SongNotFoundException();
@ -800,24 +901,23 @@ namespace NadekoBot.Modules.Music
}
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;
}
if (!silent)
{
try
{
var queuedMessage = await textCh.SendMessageAsync($"🎵`Queued`{resolvedSong.PrettyName} **at** `#{musicPlayer.Playlist.Count + 1}`").ConfigureAwait(false);
var t = Task.Run(async () =>
{
try
{
await Task.Delay(10000).ConfigureAwait(false);
await queuedMessage.DeleteAsync().ConfigureAwait(false);
}
catch { }
}).ConfigureAwait(false);
//var queuedMessage = await textCh.SendConfirmAsync($"🎵 Queued **{resolvedSong.SongInfo.Title}** at `#{musicPlayer.Playlist.Count + 1}`").ConfigureAwait(false);
var queuedMessage = await textCh.EmbedAsync(new EmbedBuilder().WithOkColor()
.WithAuthor(eab => eab.WithName("Queued Song #" + (musicPlayer.Playlist.Count + 1)).WithMusicIcon())
.WithDescription($"{resolvedSong.PrettyName}\nQueue ")
.WithThumbnail(tn => tn.Url = resolvedSong.Thumbnail)
.WithFooter(ef => ef.WithText(resolvedSong.PrettyProvider))
.Build())
.ConfigureAwait(false);
if (queuedMessage != null)
queuedMessage.DeleteAfter(10);
}
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.Xml.Linq;
using NadekoBot.Extensions;
using System.Xml;
namespace NadekoBot.Modules.NSFW
{
[NadekoModule("NSFW", "~")]
public class NSFW : DiscordModule
{
public NSFW(ILocalization loc, CommandService cmds, ShardedDiscordClient client) : base(loc, cmds, client)
public NSFW() : base()
{
}
@ -32,7 +33,7 @@ namespace NadekoBot.Modules.NSFW
var rng = new NadekoRandom();
Task<string> provider = Task.FromResult("");
switch (rng.Next(0,4))
switch (rng.Next(0, 4))
{
case 0:
provider = GetDanbooruImageLink(tag);
@ -51,11 +52,12 @@ namespace NadekoBot.Modules.NSFW
}
var link = await provider.ConfigureAwait(false);
if (string.IsNullOrWhiteSpace(link))
await channel.SendMessageAsync("Search yielded no results ;(").ConfigureAwait(false);
await channel.SendErrorAsync("No results found.").ConfigureAwait(false);
else
await channel.SendMessageAsync(link).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task HentaiBomb(IUserMessage umsg, [Remainder] string tag = null)
@ -72,45 +74,13 @@ namespace NadekoBot.Modules.NSFW
if (links.All(l => l == null))
{
await channel.SendMessageAsync("`No results.`").ConfigureAwait(false);
await channel.SendErrorAsync("No results found.").ConfigureAwait(false);
return;
}
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]
[RequireContext(ContextType.Guild)]
@ -119,54 +89,38 @@ namespace NadekoBot.Modules.NSFW
var channel = (ITextChannel)umsg.Channel;
tag = tag?.Trim() ?? "";
var link = await GetDanbooruImageLink(tag).ConfigureAwait(false);
if (string.IsNullOrWhiteSpace(link))
await channel.SendMessageAsync("Search yielded no results ;(").ConfigureAwait(false);
var url = await GetDanbooruImageLink(tag).ConfigureAwait(false);
if (url == null)
await channel.SendErrorAsync(umsg.Author.Mention + " No results.");
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]
[RequireContext(ContextType.Guild)]
public async Task Konachan(IUserMessage umsg, [Remainder] string tag = null)
{
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);
}
public Task Yandere(IUserMessage umsg, [Remainder] string tag = null)
=> Searches.Searches.InternalDapiCommand(umsg, tag, Searches.Searches.DapiSearchType.Yandere);
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Gelbooru(IUserMessage umsg, [Remainder] string tag = null)
{
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);
}
public Task Konachan(IUserMessage umsg, [Remainder] string tag = null)
=> Searches.Searches.InternalDapiCommand(umsg, tag, Searches.Searches.DapiSearchType.Konachan);
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Rule34(IUserMessage umsg, [Remainder] string tag = null)
{
var channel = (ITextChannel)umsg.Channel;
public Task Gelbooru(IUserMessage umsg, [Remainder] string tag = null)
=> Searches.Searches.InternalDapiCommand(umsg, tag, Searches.Searches.DapiSearchType.Gelbooru);
tag = tag?.Trim() ?? "";
var link = await GetRule34ImageLink(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]
[RequireContext(ContextType.Guild)]
public Task Rule34(IUserMessage umsg, [Remainder] string tag = null)
=> Searches.Searches.InternalDapiCommand(umsg, tag, Searches.Searches.DapiSearchType.Rule34);
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
@ -175,11 +129,17 @@ namespace NadekoBot.Modules.NSFW
var channel = (ITextChannel)umsg.Channel;
tag = tag?.Trim() ?? "";
var link = await GetE621ImageLink(tag).ConfigureAwait(false);
if (string.IsNullOrWhiteSpace(link))
await channel.SendMessageAsync("Search yielded no results ;(").ConfigureAwait(false);
var url = await GetE621ImageLink(tag).ConfigureAwait(false);
if (url == null)
await channel.SendErrorAsync(umsg.Author.Mention + " No results.");
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]
@ -201,13 +161,13 @@ namespace NadekoBot.Modules.NSFW
JToken obj;
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);
}
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;
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);
}
catch (Exception ex)
{
await channel.SendMessageAsync($"💢 {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;
await channel.SendErrorAsync(ex.Message).ConfigureAwait(false);
}
}
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
{
using (var http = new HttpClient())
{
http.AddFakeHeaders();
var data = await http.GetStreamAsync("http://e621.net/post/index.xml?tags=" + Uri.EscapeUriString(tags) + "%20order:random&limit=1");
var doc = XDocument.Load(data);
return doc.Descendants("file_url").FirstOrDefault().Value;
var data = await http.GetStreamAsync("https://danbooru.donmai.us/posts.xml?limit=100&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 "https://danbooru.donmai.us" + node.InnerText;
}
}
catch (Exception ex)
catch
{
Console.WriteLine("Error in e621 search: \n" + ex);
return "Error, do you have too many tags?";
return null;
}
}
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.Commands;
using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Modules.Games.Trivia;
using NadekoBot.Services;
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.Commands;
using Microsoft.EntityFrameworkCore;
using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Services;
@ -23,7 +24,7 @@ namespace NadekoBot.Modules.Permissions
public class CmdCdsCommands
{
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()
{
@ -40,13 +41,13 @@ namespace NadekoBot.Modules.Permissions
var channel = (ITextChannel)imsg.Channel;
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;
}
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>());
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>());
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
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]
@ -81,7 +86,7 @@ namespace NadekoBot.Modules.Permissions
var localSet = commandCooldowns.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet<CommandCooldown>());
if (!localSet.Any())
await channel.SendMessageAsync(" `No command cooldowns set.`").ConfigureAwait(false);
await channel.SendConfirmAsync(" `No command cooldowns set.`").ConfigureAwait(false);
else
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.Commands;
using Microsoft.EntityFrameworkCore;
using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Services;
using System.Collections.Concurrent;
using System.Linq;
@ -13,14 +15,14 @@ namespace NadekoBot.Modules.Permissions
[Group]
public class FilterCommands
{
public static ConcurrentHashSet<ulong> InviteFilteringChannels { get; set; }
public static ConcurrentHashSet<ulong> InviteFilteringServers { get; set; }
public static ConcurrentHashSet<ulong> InviteFilteringChannels { get; }
public static ConcurrentHashSet<ulong> InviteFilteringServers { get; }
//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> WordFilteringServers { get; set; }
public static ConcurrentHashSet<ulong> WordFilteringChannels { get; }
public static ConcurrentHashSet<ulong> WordFilteringServers { get; }
public static ConcurrentHashSet<string> FilteredWordsForChannel(ulong channelId, ulong guildId)
{
@ -68,7 +70,7 @@ namespace NadekoBot.Modules.Permissions
bool enabled;
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;
await uow.CompleteAsync().ConfigureAwait(false);
}
@ -76,12 +78,12 @@ namespace NadekoBot.Modules.Permissions
if (enabled)
{
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
{
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;
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);
if (removed == 0)
{
@ -109,12 +111,12 @@ namespace NadekoBot.Modules.Permissions
if (removed == 0)
{
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
{
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;
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;
await uow.CompleteAsync().ConfigureAwait(false);
}
@ -135,12 +137,12 @@ namespace NadekoBot.Modules.Permissions
if (enabled)
{
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
{
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;
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);
if (removed == 0)
{
@ -168,12 +170,12 @@ namespace NadekoBot.Modules.Permissions
if (removed == 0)
{
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
{
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;
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);
@ -206,13 +208,13 @@ namespace NadekoBot.Modules.Permissions
if (removed == 0)
{
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);
}
else
{
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);
}
}
@ -226,7 +228,7 @@ namespace NadekoBot.Modules.Permissions
ConcurrentHashSet<string> 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);
}
}

View File

@ -7,6 +7,7 @@ using NadekoBot.Services;
using Discord;
using NadekoBot.Services.Database.Models;
using System.Collections.Concurrent;
using NadekoBot.Extensions;
namespace NadekoBot.Modules.Permissions
{
@ -21,7 +22,7 @@ namespace NadekoBot.Modules.Permissions
}
//guildid, root permission
public static ConcurrentDictionary<ulong, PermissionCache> Cache;
public static ConcurrentDictionary<ulong, PermissionCache> Cache { get; }
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())
{
var config = uow.GuildConfigs.For(channel.Guild.Id);
var config = uow.GuildConfigs.For(channel.Guild.Id, set => set);
config.VerbosePermissions = action.Value;
Cache.AddOrUpdate(channel.Guild.Id, new PermissionCache()
{
@ -62,7 +63,7 @@ namespace NadekoBot.Modules.Permissions
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]
@ -72,10 +73,10 @@ namespace NadekoBot.Modules.Permissions
var channel = (ITextChannel)msg.Channel;
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)
{
await channel.SendMessageAsync($" Current permission role is **{config.PermissionRole}**.").ConfigureAwait(false);
await channel.SendConfirmAsync($" Current permission role is **{config.PermissionRole}**.").ConfigureAwait(false);
return;
}
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]
@ -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)))}"));
}
if (string.IsNullOrWhiteSpace(toSend))
await channel.SendMessageAsync("❗️`No permissions set.`").ConfigureAwait(false);
else
await channel.SendMessageAsync(toSend).ConfigureAwait(false);
}
@ -156,11 +154,11 @@ namespace NadekoBot.Modules.Permissions
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)
{
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)
{
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;
}
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;
}
}
@ -264,14 +262,14 @@ namespace NadekoBot.Modules.Permissions
}, (id, old) => { old.RootPermission = config.RootPermission; return old; });
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;
}
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]
@ -300,7 +298,7 @@ namespace NadekoBot.Modules.Permissions
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]
@ -328,7 +326,7 @@ namespace NadekoBot.Modules.Permissions
}, (id, old) => { old.RootPermission = config.RootPermission; return old; });
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]
@ -356,7 +354,7 @@ namespace NadekoBot.Modules.Permissions
}, (id, old) => { old.RootPermission = config.RootPermission; return old; });
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]
@ -384,7 +382,7 @@ namespace NadekoBot.Modules.Permissions
}, (id, old) => { old.RootPermission = config.RootPermission; return old; });
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]
@ -412,7 +410,7 @@ namespace NadekoBot.Modules.Permissions
}, (id, old) => { old.RootPermission = config.RootPermission; return old; });
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]
@ -440,7 +438,7 @@ namespace NadekoBot.Modules.Permissions
}, (id, old) => { old.RootPermission = config.RootPermission; return old; });
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]
@ -471,9 +469,9 @@ namespace NadekoBot.Modules.Permissions
}
}
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]
@ -501,7 +499,7 @@ namespace NadekoBot.Modules.Permissions
}, (id, old) => { old.RootPermission = config.RootPermission; return old; });
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]
@ -529,7 +527,7 @@ namespace NadekoBot.Modules.Permissions
}, (id, old) => { old.RootPermission = config.RootPermission; return old; });
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]
@ -557,7 +555,7 @@ namespace NadekoBot.Modules.Permissions
}, (id, old) => { old.RootPermission = config.RootPermission; return old; });
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]
@ -585,7 +583,7 @@ namespace NadekoBot.Modules.Permissions
}, (id, old) => { old.RootPermission = config.RootPermission; return old; });
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]
@ -624,7 +622,7 @@ namespace NadekoBot.Modules.Permissions
}, (id, old) => { old.RootPermission = config.RootPermission; return old; });
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.Collections.Generic;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
namespace NadekoBot.Modules.Searches
@ -19,14 +20,37 @@ namespace NadekoBot.Modules.Searches
[Group]
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; }
private DateTime lastRefresh { get; set; }
public AnimeSearchCommands()
static AnimeSearchCommands()
{
_log = LogManager.GetCurrentClassLogger();
anilistTokenRefresher = new Timer(async (state) =>
{
try
{
var headers = new Dictionary<string, string> {
{"grant_type", "client_credentials"},
{"client_id", "kwoth-w0ki9"},
{"client_secret", "Qd6j4FIAi1ZK6Pc7N7V4Z"},
};
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]
@ -40,34 +64,22 @@ namespace NadekoBot.Modules.Searches
var animeData = await GetAnimeData(query).ConfigureAwait(false);
var embed = new Discord.API.Embed()
if (animeData == null)
{
Description = animeData.Synopsis,
Title = animeData.title_english,
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)
await umsg.Channel.SendErrorAsync("Failed finding that animu.").ConfigureAwait(false);
return;
}
},
Color = NadekoBot.OkColor
};
await channel.EmbedAsync(embed).ConfigureAwait(false);
var embed = new EmbedBuilder().WithColor(NadekoBot.OkColor)
.WithDescription(animeData.Synopsis.Replace("<br>", Environment.NewLine))
.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]
@ -79,38 +91,25 @@ namespace NadekoBot.Modules.Searches
if (string.IsNullOrWhiteSpace(query))
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,
Title = animeData.title_english,
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)
await umsg.Channel.SendErrorAsync("Failed finding that mango.").ConfigureAwait(false);
return;
}
},
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)
@ -119,7 +118,6 @@ namespace NadekoBot.Modules.Searches
throw new ArgumentNullException(nameof(query));
try
{
await RefreshAnilistToken().ConfigureAwait(false);
var link = "http://anilist.co/api/anime/search/" + Uri.EscapeUriString(query);
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);
}
}
catch (Exception ex) {
catch (Exception ex)
{
_log.Warn(ex, "Failed anime search for {0}", query);
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)
{
if (string.IsNullOrWhiteSpace(query))
throw new ArgumentNullException(nameof(query));
try
{
await RefreshAnilistToken().ConfigureAwait(false);
using (var http = new HttpClient())
{
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.Commands;
using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Modules.Searches.Models;
using NadekoBot.Services;
using Newtonsoft.Json;
@ -19,11 +20,11 @@ namespace NadekoBot.Modules.Searches
[Group]
public class JokeCommands
{
private List<WoWJoke> wowJokes = new List<WoWJoke>();
private List<MagicItem> magicItems;
private Logger _log;
private static List<WoWJoke> wowJokes { get; } = new List<WoWJoke>();
private static List<MagicItem> magicItems { get; } = new List<MagicItem>();
private static Logger _log { get; }
public JokeCommands()
static JokeCommands()
{
_log = LogManager.GetCurrentClassLogger();
if (File.Exists("data/wowjokes.json"))
@ -43,61 +44,63 @@ namespace NadekoBot.Modules.Searches
[NadekoCommand, Usage, Description, Aliases]
[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())
{
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]
[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())
{
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]
[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())
{
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]
[RequireContext(ContextType.Guild)]
public async Task WowJoke(IUserMessage umsg)
public async Task WowJoke(IUserMessage msg)
{
var channel = (ITextChannel)umsg.Channel;
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]
[RequireContext(ContextType.Guild)]
public async Task MagicItem(IUserMessage umsg)
public async Task MagicItem(IUserMessage msg)
{
var channel = (ITextChannel)umsg.Channel;
var rng = new NadekoRandom();
var item = magicItems[rng.Next(0, magicItems.Count)].ToString();
if (!wowJokes.Any())
{
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.Commands;
using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Services;
using Newtonsoft.Json.Linq;
using System;
@ -23,7 +24,7 @@ namespace NadekoBot.Modules.Searches
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.",
"DONT READ BELOW! Ban Urgot mid OP 100%. Im smurf Diamond 1.",
"Ask your teammates what would they like to play, and ban that.",
@ -50,23 +51,19 @@ namespace NadekoBot.Modules.Searches
$"limit={showCount}")
.ConfigureAwait(false))["data"] as JArray;
var dataList = data.Distinct(new ChampionNameComparer()).Take(showCount).ToList();
var sb = new StringBuilder();
sb.AppendLine($"**Showing {dataList.Count} top banned champions.**");
sb.AppendLine($"`{trashTalk[new NadekoRandom().Next(0, trashTalk.Length)]}`");
var eb = new EmbedBuilder().WithOkColor().WithTitle(Format.Underline($"{dataList.Count} most banned champions"));
for (var i = 0; i < dataList.Count; i++)
{
if (i % 2 == 0 && i != 0)
sb.AppendLine();
sb.Append($"`{i + 1}.` **{dataList[i]["name"]}** {dataList[i]["general"]["banRate"]}% ");
//sb.AppendLine($" ({dataList[i]["general"]["banRate"]}%)");
var champ = dataList[i];
eb.AddField(efb => efb.WithName(champ["name"].ToString()).WithValue(champ["general"]["banRate"] + "%").WithIsInline(true));
}
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)
{
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))
{
http.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
string rawJson = "";
try
{
rawJson = await http.GetStringAsync("https://memegen.link/api/templates/").ConfigureAwait(false);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
var rawJson = await http.GetStringAsync("https://memegen.link/api/templates/").ConfigureAwait(false);
var data = JsonConvert.DeserializeObject<Dictionary<string, string>>(rawJson)
.Select(kvp => Path.GetFileName(kvp.Value));
await 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 top = Uri.EscapeDataString(topText.Replace(' ', '-'));
var bot = Uri.EscapeDataString(botText.Replace(' ', '-'));
await channel.SendMessageAsync($"http://memegen.link/{meme}/{top}/{bot}.jpg");
var top = topText.Replace(' ', '-');
var bot = botText.Replace(' ', '-');
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 image_url_lge;
public string[] Genres;
public string average_score;
public string Link => "http://anilist.co/anime/" + id;
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 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 string description;
public string[] Genres;
public string average_score;
public string Link => "http://anilist.co/manga/" + id;
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,9 +18,8 @@ namespace NadekoBot.Modules.Searches.Models
public int SPD { get; set; }
public int SPE { get; set; }
public override string ToString() => $@"
**HP:** {HP,-4} **ATK:** {ATK,-4} **DEF:** {DEF,-4}
**SPA:** {SPA,-4} **SPD:** {SPD,-4} **SPE:** {SPE,-4}";
public override string ToString() => $@"**HP:** {HP,-4} **ATK:** {ATK,-4} **DEF:** {DEF,-4}
**SPA:** {SPA,-4} **SPD:** {SPD,-4} **SPE:** {SPE,-4}";
}
public int Id { get; set; }
public string Species { 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.Net.Http;
using System.Threading.Tasks;
@ -32,6 +35,17 @@ namespace NadekoBot.Modules.Searches.Commands.OMDB
public string Plot { 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() =>
$@"`Title:` {Title}
`Year:` {Year}

View File

@ -18,9 +18,9 @@ namespace NadekoBot.Modules.Searches
[Group]
public class OsuCommands
{
private Logger _log;
private static Logger _log { get; }
public OsuCommands()
static OsuCommands()
{
_log = LogManager.GetCurrentClassLogger();
}
@ -52,7 +52,7 @@ namespace NadekoBot.Modules.Searches
}
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");
}
}
@ -66,7 +66,7 @@ namespace NadekoBot.Modules.Searches
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;
}
@ -90,7 +90,7 @@ namespace NadekoBot.Modules.Searches
}
catch (Exception ex)
{
await channel.SendMessageAsync("Something went wrong.");
await channel.SendErrorAsync("Something went wrong.");
_log.Warn(ex, "Osub command failed");
}
}
@ -102,13 +102,13 @@ namespace NadekoBot.Modules.Searches
var channel = (ITextChannel)umsg.Channel;
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;
}
if (string.IsNullOrWhiteSpace(user))
{
await channel.SendMessageAsync("💢 Please provide a username.").ConfigureAwait(false);
await channel.SendErrorAsync("Please provide a username.").ConfigureAwait(false);
return;
}
using (var http = new HttpClient())
@ -141,7 +141,7 @@ namespace NadekoBot.Modules.Searches
}
catch (Exception ex)
{
await channel.SendMessageAsync("Something went wrong.");
await channel.SendErrorAsync("Something went wrong.");
_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.Commands;
using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Services;
using System;
using System.Threading.Tasks;
@ -36,7 +37,7 @@ namespace NadekoBot.Modules.Searches
{
var channel = (ITextChannel)imsg.Channel;
await channel.SendMessageAsync(typesStr)
await channel.SendConfirmAsync(typesStr)
.ConfigureAwait(false);
}

View File

@ -1,11 +1,13 @@
using Discord;
using Discord.Commands;
using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Modules.Searches.Models;
using Newtonsoft.Json;
using NLog;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace NadekoBot.Modules.Searches
@ -15,15 +17,15 @@ namespace NadekoBot.Modules.Searches
[Group]
public class PokemonSearchCommands
{
private static Dictionary<string, SearchPokemon> pokemons = new Dictionary<string, SearchPokemon>();
private static Dictionary<string, SearchPokemonAbility> pokemonAbilities = new Dictionary<string, SearchPokemonAbility>();
private static Dictionary<string, SearchPokemon> pokemons { get; } = new Dictionary<string, SearchPokemon>();
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";
private Logger _log;
public const string PokemonListFile = "data/pokemon/pokemon_list7.json";
private static Logger _log { get; }
public PokemonSearchCommands()
static PokemonSearchCommands()
{
_log = LogManager.GetCurrentClassLogger();
if (File.Exists(PokemonListFile))
@ -52,11 +54,18 @@ namespace NadekoBot.Modules.Searches
{
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;
}
}
await channel.SendMessageAsync("`No pokemon found.`");
await channel.SendErrorAsync("No pokemon found.");
}
[NadekoCommand, Usage, Description, Aliases]
@ -72,11 +81,15 @@ namespace NadekoBot.Modules.Searches
{
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;
}
}
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 Discord.WebSocket;
using NadekoBot.Attributes;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
using NLog;
using NadekoBot.Services.Database;
using NadekoBot.Extensions;
namespace NadekoBot.Modules.Searches
{
@ -19,128 +24,171 @@ namespace NadekoBot.Modules.Searches
{
public class StreamStatus
{
public StreamStatus(string link, bool isLive, string views)
{
Link = link;
IsLive = isLive;
Views = views;
}
public bool IsLive { get; set; }
public string Link { get; set; }
public string ApiLink { 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]
public class StreamNotificationCommands
{
private Timer checkTimer { get; }
private ConcurrentDictionary<string, StreamStatus> oldCachedStatuses = new ConcurrentDictionary<string, StreamStatus>();
private ConcurrentDictionary<string, StreamStatus> cachedStatuses = new ConcurrentDictionary<string, StreamStatus>();
private bool FirstPass { get; set; } = true;
private static Timer checkTimer { get; }
private static ConcurrentDictionary<string, StreamStatus> oldCachedStatuses = new ConcurrentDictionary<string, StreamStatus>();
private static ConcurrentDictionary<string, StreamStatus> cachedStatuses = new ConcurrentDictionary<string, StreamStatus>();
private static Logger _log { get; }
public StreamNotificationCommands()
private static bool FirstPass { get; set; } = true;
static StreamNotificationCommands()
{
_log = NLog.LogManager.GetCurrentClassLogger();
checkTimer = new Timer(async (state) =>
{
oldCachedStatuses = new ConcurrentDictionary<string, StreamStatus>(cachedStatuses);
cachedStatuses = new ConcurrentDictionary<string, StreamStatus>();
try
{
cachedStatuses.Clear();
IEnumerable<FollowedStream> streams;
using (var uow = DbHandler.UnitOfWork())
{
streams = uow.GuildConfigs.GetAllFollowedStreams();
}
foreach (var stream in streams)
await Task.WhenAll(streams.Select(async fs =>
{
StreamStatus data;
try
{
data = await GetStreamStatus(stream).ConfigureAwait(false);
if (data == null)
var newStatus = await GetStreamStatus(fs).ConfigureAwait(false);
if (FirstPass)
{
return;
}
catch
{
continue;
}
StreamStatus oldData;
oldCachedStatuses.TryGetValue(data.Link, out oldData);
if (oldData == null || data.IsLive != oldData.IsLive)
StreamStatus oldStatus;
if (oldCachedStatuses.TryGetValue(newStatus.ApiLink, out oldStatus) &&
oldStatus.IsLive != newStatus.IsLive)
{
if (FirstPass)
continue;
var server = NadekoBot.Client.GetGuild(stream.GuildId);
var channel = server?.GetTextChannel(stream.ChannelId);
var server = NadekoBot.Client.GetGuild(fs.GuildId);
var channel = server?.GetTextChannel(fs.ChannelId);
if (channel == null)
continue;
var msg = $"`{stream.Username}`'s stream is now " +
$"**{(data.IsLive ? "ONLINE" : "OFFLINE")}** with " +
$"**{data.Views}** viewers.";
if (data.IsLive)
if (stream.Type == FollowedStream.FollowedStreamType.Hitbox)
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;
return;
try
{
var msg = await channel.EmbedAsync(fs.GetEmbed(newStatus).Build()).ConfigureAwait(false);
if (!newStatus.IsLive)
msg.DeleteAfter(60);
}
catch { }
}
}
catch { }
}));
FirstPass = false;
}, 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;
JObject data;
StreamStatus result;
switch (stream.Type)
{
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))
return result;
using (var http = new HttpClient())
{
response = await http.GetStringAsync(hitboxUrl).ConfigureAwait(false);
}
data = JObject.Parse(response);
isLive = data["media_is_live"].ToString() == "1";
result = new StreamStatus(hitboxUrl, isLive, data["media_views"].ToString());
cachedStatuses.TryAdd(hitboxUrl, result);
var hbData = JsonConvert.DeserializeObject<HitboxResponse>(response);
if (!hbData.Success)
throw new StreamNotFoundException($"{stream.Username} [{stream.Type}]");
result = new StreamStatus()
{
IsLive = hbData.IsLive,
ApiLink = hitboxUrl,
Views = hbData.Views
};
cachedStatuses.AddOrUpdate(hitboxUrl, result, (key, old) => result);
return result;
case FollowedStream.FollowedStreamType.Twitch:
var twitchUrl = $"https://api.twitch.tv/kraken/streams/{Uri.EscapeUriString(stream.Username)}?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))
return result;
using (var http = new HttpClient())
{
response = await http.GetStringAsync(twitchUrl).ConfigureAwait(false);
}
data = JObject.Parse(response);
isLive = !string.IsNullOrWhiteSpace(data["stream"].ToString());
result = new StreamStatus(twitchUrl, isLive, isLive ? data["stream"]["viewers"].ToString() : "0");
cachedStatuses.TryAdd(twitchUrl, result);
var twData = JsonConvert.DeserializeObject<TwitchResponse>(response);
if (twData.Error != null)
{
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;
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))
return result;
using (var http = new HttpClient())
{
response = await http.GetStringAsync(beamUrl).ConfigureAwait(false);
}
data = JObject.Parse(response);
isLive = data["online"].ToObject<bool>() == true;
result = new StreamStatus(beamUrl, isLive, data["viewersCurrent"].ToString());
cachedStatuses.TryAdd(beamUrl, result);
var bmData = JsonConvert.DeserializeObject<BeamResponse>(response);
if (bmData.Error != null)
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;
default:
break;
@ -178,12 +226,15 @@ namespace NadekoBot.Modules.Searches
IEnumerable<FollowedStream> streams;
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())
{
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;
}
@ -192,36 +243,39 @@ namespace NadekoBot.Modules.Searches
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]
[RequireContext(ContextType.Guild)]
[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;
username = username.ToLowerInvariant().Trim();
FollowedStream toRemove;
var fs = new FollowedStream()
{
ChannelId = channel.Id,
Username = username,
Type = type
};
bool removed;
using (var uow = DbHandler.UnitOfWork())
{
var config = uow.GuildConfigs.For(channel.Guild.Id);
var streams = config.FollowedStreams;
toRemove = streams.Where(fs => fs.ChannelId == channel.Id && fs.Username.ToLowerInvariant() == username).FirstOrDefault();
if (toRemove != null)
{
config.FollowedStreams = new HashSet<FollowedStream>(streams.Except(new[] { toRemove }));
await uow.CompleteAsync();
var config = uow.GuildConfigs.For(channel.Guild.Id, set => set.Include(gc => gc.FollowedStreams));
removed = config.FollowedStreams.Remove(fs);
if (removed)
await uow.CompleteAsync().ConfigureAwait(false);
}
}
if (toRemove == null)
if (!removed)
{
await channel.SendMessageAsync(":anger: No such stream.").ConfigureAwait(false);
await channel.SendErrorAsync("No such stream.").ConfigureAwait(false);
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]
@ -238,69 +292,86 @@ namespace NadekoBot.Modules.Searches
var streamStatus = (await GetStreamStatus(new FollowedStream
{
Username = stream,
Type = platform
Type = platform,
}));
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
{
await channel.SendMessageAsync($"`Streamer {username} is offline.`");
await channel.SendConfirmAsync($"Streamer {username} is offline.");
}
}
catch
{
await channel.SendMessageAsync("No channel found.");
await channel.SendErrorAsync("No channel found.");
}
}
private async Task TrackStream(ITextChannel channel, string username, FollowedStream.FollowedStreamType type)
{
username = username.ToLowerInvariant().Trim();
var stream = new FollowedStream
var fs = new FollowedStream
{
GuildId = channel.Guild.Id,
ChannelId = channel.Id,
Username = username,
Type = type,
};
bool exists;
using (var uow = DbHandler.UnitOfWork())
{
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;
StreamStatus status;
try
{
data = await GetStreamStatus(stream).ConfigureAwait(false);
status = await GetStreamStatus(fs).ConfigureAwait(false);
}
catch
{
await channel.SendMessageAsync(":anger: Stream probably doesn't exist.").ConfigureAwait(false);
await channel.SendErrorAsync("Stream probably doesn't exist.").ConfigureAwait(false);
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())
{
uow.GuildConfigs.For(channel.Guild.Id).FollowedStreams.Add(stream);
await uow.CompleteAsync();
uow.GuildConfigs.For(channel.Guild.Id, set => set.Include(gc => gc.FollowedStreams))
.FollowedStreams
.Add(fs);
await uow.CompleteAsync().ConfigureAwait(false);
}
msg = $":ok: I will notify this channel when status changes.\n{msg}";
await channel.SendMessageAsync(msg).ConfigureAwait(false);
await channel.EmbedAsync(fs.GetEmbed(status).Build(), $"🆗 I will notify this channel when status changes.").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>();
UserLanguages = new ConcurrentDictionary<UserChannelPair, string>();
NadekoBot.Client.MessageReceived += (msg) =>
NadekoBot.Client.MessageReceived += async (msg) =>
{
try
{
var umsg = msg as IUserMessage;
if(umsg == null)
return Task.CompletedTask;
if (umsg == null)
return;
bool autoDelete;
if (!TranslatedChannels.TryGetValue(umsg.Channel.Id, out autoDelete))
return Task.CompletedTask;
var t = Task.Run(async () =>
{
return;
var key = new UserChannelPair()
{
UserId = umsg.Author.Id,
@ -50,18 +49,13 @@ namespace NadekoBot.Modules.Searches
if (!UserLanguages.TryGetValue(key, out langs))
return;
try
{
var text = await TranslateInternal(umsg, langs, umsg.Resolve(UserMentionHandling.Ignore), true)
.ConfigureAwait(false);
if (autoDelete)
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 { }
});
return Task.CompletedTask;
};
}
@ -75,12 +69,11 @@ namespace NadekoBot.Modules.Searches
{
await umsg.Channel.TriggerTypingAsync().ConfigureAwait(false);
var translation = await TranslateInternal(umsg, langs, text);
await channel.SendMessageAsync(translation).ConfigureAwait(false);
await channel.SendConfirmAsync("Translation " + langs, translation).ConfigureAwait(false);
}
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();
if (string.IsNullOrWhiteSpace(text))
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
@ -114,19 +107,19 @@ namespace NadekoBot.Modules.Searches
if (autoDelete == AutoDeleteAutoTranslate.Del)
{
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;
}
bool 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;
}
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 (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;
}
@ -157,13 +150,13 @@ namespace NadekoBot.Modules.Searches
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;
}
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]

View File

@ -1,6 +1,7 @@
using Discord;
using Discord.Commands;
using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Services;
using Newtonsoft.Json;
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 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);
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 System.IO;
using NadekoBot.Modules.Searches.Commands.OMDB;
using NadekoBot.Modules.Searches.Commands.Models;
using AngleSharp.Parser.Html;
using AngleSharp;
using AngleSharp.Dom.Html;
using AngleSharp.Dom;
using System.Xml;
using System.Xml.Linq;
namespace NadekoBot.Modules.Searches
{
[NadekoModule("Searches", "~")]
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]
[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;
city = city.Replace(" ", "");
country = city.Replace(" ", "");
if (string.IsNullOrWhiteSpace(query))
return;
string response;
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()
.AddField(fb => fb.WithName("🌍 Location").WithValue($"{obj["target"]}").WithIsInline(true))
.AddField(fb => fb.WithName("📏 Lat,Long").WithValue($"{obj["latitude"]}, {obj["longitude"]}").WithIsInline(true))
.AddField(fb => fb.WithName("☁ Condition").WithValue($"{obj["condition"]}").WithIsInline(true))
.AddField(fb => fb.WithName("😓 Humidity").WithValue($"{obj["humidity"]}%").WithIsInline(true))
.AddField(fb => fb.WithName("💨 Wind Speed").WithValue($"{obj["windspeedk"]}km/h / {obj["windspeedm"]}mph").WithIsInline(true))
.AddField(fb => fb.WithName("🌡 Temperature").WithValue($"{obj["centigrade"]}°C / {obj["fahrenheit"]}°F").WithIsInline(true))
.AddField(fb => fb.WithName("🔆 Feels like").WithValue($"{obj["feelscentigrade"]}°C / {obj["feelsfahrenheit"]}°F").WithIsInline(true))
.AddField(fb => fb.WithName("🌄 Sunrise").WithValue($"{obj["sunrise"]}").WithIsInline(true))
.AddField(fb => fb.WithName("🌇 Sunset").WithValue($"{obj["sunset"]}").WithIsInline(true))
.WithColor(NadekoBot.OkColor);
.AddField(fb => fb.WithName("🌍 **Location**").WithValue(data.name + ", " + data.sys.country).WithIsInline(true))
.AddField(fb => fb.WithName("📏 **Lat,Long**").WithValue($"{data.coord.lat}, {data.coord.lon}").WithIsInline(true))
.AddField(fb => fb.WithName("☁ **Condition**").WithValue(String.Join(", ", data.weather.Select(w => w.main))).WithIsInline(true))
.AddField(fb => fb.WithName("😓 **Humidity**").WithValue($"{data.main.humidity}%").WithIsInline(true))
.AddField(fb => fb.WithName("💨 **Wind Speed**").WithValue(data.wind.speed + " km/h").WithIsInline(true))
.AddField(fb => fb.WithName("🌡 **Temperature**").WithValue(data.main.temp + "°C").WithIsInline(true))
.AddField(fb => fb.WithName("🔆 **Min - Max**").WithValue($"{data.main.temp_min}°C - {data.main.temp_max}°C").WithIsInline(true))
.AddField(fb => fb.WithName("🌄 **Sunrise (utc)**").WithValue($"{data.sys.sunrise.ToUnixTimestamp():HH:mm}").WithIsInline(true))
.AddField(fb => fb.WithName("🌇 **Sunset (utc)**").WithValue($"{data.sys.sunset.ToUnixTimestamp():HH:mm}").WithIsInline(true))
.WithOkColor()
.WithFooter(efb => efb.WithText("Powered by http://openweathermap.org"));
await channel.EmbedAsync(embed.Build()).ConfigureAwait(false);
}
@ -63,13 +65,16 @@ namespace NadekoBot.Modules.Searches
{
var channel = (ITextChannel)umsg.Channel;
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))
{
await channel.SendMessageAsync("No results found for that query.");
await channel.SendErrorAsync("No results found for that query.").ConfigureAwait(false);
return;
}
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]
@ -84,10 +89,10 @@ namespace NadekoBot.Modules.Searches
var movie = await OmdbProvider.FindMovie(query);
if (movie == null)
{
await channel.SendMessageAsync("Failed to find that movie.").ConfigureAwait(false);
await channel.SendErrorAsync("Failed to find that movie.").ConfigureAwait(false);
return;
}
await channel.SendMessageAsync(movie.ToString()).ConfigureAwait(false);
await channel.EmbedAsync(movie.GetEmbed()).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
@ -97,9 +102,8 @@ namespace NadekoBot.Modules.Searches
var channel = (ITextChannel)umsg.Channel;
using (var http = new HttpClient())
{
await channel.SendMessageAsync(JObject.Parse(
await http.GetStringAsync("http://www.random.cat/meow").ConfigureAwait(false))["file"].ToString())
.ConfigureAwait(false);
var res = JObject.Parse(await http.GetStringAsync("http://www.random.cat/meow").ConfigureAwait(false));
await channel.SendMessageAsync(res["file"].ToString()).ConfigureAwait(false);
}
}
@ -110,7 +114,8 @@ namespace NadekoBot.Modules.Searches
var channel = (ITextChannel)umsg.Channel;
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)"))
{
await channel.SendMessageAsync("Daily limit reached!");
await channel.SendErrorAsync("Daily limit reached!");
}
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)"))
{
await channel.SendMessageAsync("Daily limit reached!");
await channel.SendErrorAsync("Daily limit reached!");
}
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))
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);
}
@ -197,21 +204,78 @@ namespace NadekoBot.Modules.Searches
if (string.IsNullOrWhiteSpace(arg))
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]
[RequireContext(ContextType.Guild)]
public async Task Google(IUserMessage umsg, [Remainder] string terms = null)
{
var channel = (ITextChannel)umsg.Channel;
terms = terms?.Trim();
if (string.IsNullOrWhiteSpace(terms))
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);
await channel.EmbedAsync(embed.WithDescription(String.Concat(desc)).Build()).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
@ -222,7 +286,7 @@ namespace NadekoBot.Modules.Searches
var arg = name;
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;
}
@ -238,18 +302,26 @@ namespace NadekoBot.Modules.Searches
var items = JArray.Parse(response).Shuffle().ToList();
if (items == null)
throw new KeyNotFoundException("Cannot find a card by that name");
var msg = $@"```css
[☕ Magic The Gathering]: {items[0]["name"].ToString()}
[Store URL]: {await _google.ShortenUrl(items[0]["store_url"].ToString())}
[Cost]: {items[0]["cost"].ToString()}
[Description]: {items[0]["text"].ToString()}
```
{items[0]["editions"][0]["image_url"].ToString()}";
await channel.SendMessageAsync(msg).ConfigureAwait(false);
var item = items[0];
var storeUrl = await NadekoBot.Google.ShortenUrl(item["store_url"].ToString());
var cost = item["cost"].ToString();
var desc = item["text"].ToString();
var types = String.Join(",\n", item["types"].ToObject<string[]>());
var img = item["editions"][0]["image_url"].ToString();
var embed = new EmbedBuilder().WithOkColor()
.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
{
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;
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;
}
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;
}
@ -308,7 +380,49 @@ namespace NadekoBot.Modules.Searches
}
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))
{
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;
}
var arg = query;
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;
}
await umsg.Channel.TriggerTypingAsync().ConfigureAwait(false);
using (var http = new HttpClient())
{
http.DefaultRequestHeaders.Clear();
http.DefaultRequestHeaders.Add("X-Mashape-Key", NadekoBot.Credentials.MashapeKey);
var res = await http.GetStringAsync($"https://mashape-community-urban-dictionary.p.mashape.com/define?term={Uri.EscapeUriString(arg)}").ConfigureAwait(false);
http.DefaultRequestHeaders.Add("Accept", "application/json");
var res = await http.GetStringAsync($"http://api.urbandictionary.com/v0/define?term={Uri.EscapeUriString(arg)}").ConfigureAwait(false);
try
{
var items = JObject.Parse(res);
var sb = new StringBuilder();
sb.AppendLine($"`Term:` {items["list"][0]["word"].ToString()}");
sb.AppendLine($"`Definition:` {items["list"][0]["definition"].ToString()}");
sb.Append($"`Link:` <{await _google.ShortenUrl(items["list"][0]["permalink"].ToString()).ConfigureAwait(false)}>");
await channel.SendMessageAsync(sb.ToString());
var item = items["list"][0];
var word = item["word"].ToString();
var def = item["definition"].ToString();
var link = item["permalink"].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
{
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]
[RequireContext(ContextType.Guild)]
public async Task Hashtag(IUserMessage umsg, [Remainder] string query = null)
@ -362,12 +516,12 @@ namespace NadekoBot.Modules.Searches
var arg = query;
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;
}
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;
}
@ -383,14 +537,20 @@ namespace NadekoBot.Modules.Searches
try
{
var items = JObject.Parse(res);
var str = $@"`Hashtag:` {items["defs"]["def"]["hashtag"].ToString()}
`Definition:` {items["defs"]["def"]["text"].ToString()}
`Link:` <{await _google.ShortenUrl(items["defs"]["def"]["uri"].ToString()).ConfigureAwait(false)}>";
await channel.SendMessageAsync(str);
var item = items["defs"]["def"];
var hashtag = item["hashtag"].ToString();
var link = item["uri"].ToString();
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
{
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);
if (response == null)
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)
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]
@ -428,22 +590,13 @@ namespace NadekoBot.Modules.Searches
if (string.IsNullOrWhiteSpace(imageLink))
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]
[RequireContext(ContextType.Guild)]
public async Task Safebooru(IUserMessage umsg, [Remainder] string tag = null)
{
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);
}
public Task Safebooru(IUserMessage umsg, [Remainder] string tag = null)
=> InternalDapiCommand(umsg, tag, DapiSearchType.Safebooru);
[NadekoCommand, Usage, Description, Aliases]
[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 data = JsonConvert.DeserializeObject<WikipediaApiModel>(result);
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
await channel.SendMessageAsync(data.Query.Pages[0].FullUrl);
}
@ -499,12 +652,12 @@ namespace NadekoBot.Modules.Searches
str += new NadekoRandom().Next();
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)
{
Console.WriteLine(ex);
_log.Error(ex);
}
}
@ -517,180 +670,10 @@ namespace NadekoBot.Modules.Searches
var usr = umsg.MentionedUsers.FirstOrDefault();
if (usr == null)
{
await channel.SendMessageAsync("Invalid user specified.").ConfigureAwait(false);
await channel.SendErrorAsync("Invalid user specified.").ConfigureAwait(false);
return;
}
await channel.SendMessageAsync(await _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);
}
}
await channel.SendMessageAsync(await NadekoBot.Google.ShortenUrl(usr.AvatarUrl).ConfigureAwait(false)).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
@ -700,7 +683,7 @@ ELO: [{playerELO.ToString()}]
var channel = (ITextChannel)umsg.Channel;
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;
}
await umsg.Channel.TriggerTypingAsync().ConfigureAwait(false);
@ -719,7 +702,7 @@ ELO: [{playerELO.ToString()}]
}
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;
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;
}
await umsg.Channel.TriggerTypingAsync().ConfigureAwait(false);
@ -756,7 +739,7 @@ ELO: [{playerELO.ToString()}]
}
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;
if (string.IsNullOrWhiteSpace(arg))
{
await channel.SendMessageAsync("💢 Please enter a `ip:port`.").ConfigureAwait(false);
await channel.SendErrorAsync("Please enter `ip:port`.").ConfigureAwait(false);
return;
}
await umsg.Channel.TriggerTypingAsync().ConfigureAwait(false);
@ -796,15 +779,86 @@ ELO: [{playerELO.ToString()}]
}
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)
{
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;
}
}

View File

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

View File

@ -10,58 +10,46 @@ using System.Threading.Tasks;
namespace NadekoBot.Modules.Utility
{
partial class Utility : DiscordModule
public partial class Utility
{
[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.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)
public async Task ServerInfo(IUserMessage msg, string guildName = null)
{
var channel = (ITextChannel)msg.Channel;
guild = guild?.ToUpperInvariant();
IGuild server;
if (guild == null)
server = channel.Guild;
guildName = guildName?.ToUpperInvariant();
IGuild guild;
if (string.IsNullOrWhiteSpace(guildName))
guild = channel.Guild;
else
server = _client.GetGuilds().Where(g => g.Name.ToUpperInvariant() == guild.ToUpperInvariant()).FirstOrDefault();
if (server == null)
guild = NadekoBot.Client.GetGuilds().Where(g => g.Name.ToUpperInvariant() == guildName.ToUpperInvariant()).FirstOrDefault();
if (guild == null)
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 users = await server.GetUsersAsync();
sb.AppendLine($@"__`Name:`__ **{server.Name}**
__`Owner:`__ **{await server.GetUserAsync(server.OwnerId)}**
__`ID:`__ **{server.Id}**
__`Icon URL:`__ { server.IconUrl}
__`TextChannels:`__ **{(await server.GetTextChannelsAsync()).Count()}** `VoiceChannels:` **{(await server.GetVoiceChannelsAsync()).Count()}**
__`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)}
__`Roles:`__ **{server.Roles.Count()}**
__`Created At:`__ **{createdAt.ToString("dd.MM.yyyy HH:mm")}**
");
if (server.Emojis.Count() > 0)
sb.AppendLine($"__`Custom Emojis:`__ *{string.Join(", ", server.Emojis)}*");
if (server.Features.Count() > 0)
sb.AppendLine($"__`Features:`__ **{string.Join(", ", server.Features)}**");
if (!string.IsNullOrWhiteSpace(server.SplashUrl))
sb.AppendLine($"__`Region:`__ **{server.VoiceRegionId}**");
await msg.Reply(sb.ToString()).ConfigureAwait(false);
var users = await guild.GetUsersAsync().ConfigureAwait(false);
var embed = new EmbedBuilder()
.WithAuthor(eab => eab.WithName("Server Info"))
.WithTitle(guild.Name)
.AddField(fb => fb.WithName("**ID**").WithValue(guild.Id.ToString()).WithIsInline(true))
.AddField(fb => fb.WithName("**Owner**").WithValue(ownername.ToString()).WithIsInline(true))
.AddField(fb => fb.WithName("**Members**").WithValue(users.Count.ToString()).WithIsInline(true))
.AddField(fb => fb.WithName("**Text Channels**").WithValue(textchn.ToString()).WithIsInline(true))
.AddField(fb => fb.WithName("**Voice Channels**").WithValue(voicechn.ToString()).WithIsInline(true))
.AddField(fb => fb.WithName("**Created At**").WithValue($"{createdAt.ToString("dd.MM.yyyy HH:mm")}").WithIsInline(true))
.AddField(fb => fb.WithName("**Region**").WithValue(guild.VoiceRegionId.ToString()).WithIsInline(true))
.AddField(fb => fb.WithName("**Roles**").WithValue(guild.Roles.Count().ToString()).WithIsInline(true))
.WithImage(tn => tn.WithUrl(guild.IconUrl))
.WithOkColor();
if (guild.Emojis.Count() > 0)
{
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]
@ -72,12 +60,15 @@ __`Created At:`__ **{createdAt.ToString("dd.MM.yyyy HH:mm")}**
if (ch == null)
return;
var createdAt = new DateTime(2015, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(ch.Id >> 22);
var toReturn = $@"__`Name:`__ **#{ch.Name}**
__`ID:`__ **{ch.Id}**
__`Created At:`__ **{createdAt.ToString("dd.MM.yyyy HH:mm")}**
__`Topic:`__ {ch.Topic}
__`Users:`__ **{(await ch.GetUsersAsync()).Count()}**";
await msg.Reply(toReturn).ConfigureAwait(false);
var usercount = (await ch.GetUsersAsync()).Count();
var embed = new EmbedBuilder()
.WithTitle(ch.Name)
.WithDescription(ch.Topic?.SanitizeMentions())
.AddField(fb => fb.WithName("**ID**").WithValue(ch.Id.ToString()).WithIsInline(true))
.AddField(fb => fb.WithName("**Created At**").WithValue($"{createdAt.ToString("dd.MM.yyyy HH:mm")}").WithIsInline(true))
.AddField(fb => fb.WithName("**Users**").WithValue(usercount.ToString()).WithIsInline(true))
.WithOkColor();
await msg.Channel.EmbedAsync(embed.Build()).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
@ -86,20 +77,49 @@ __`Users:`__ **{(await ch.GetUsersAsync()).Count()}**";
{
var channel = (ITextChannel)msg.Channel;
var user = usr ?? msg.Author as IGuildUser;
if (user == null)
return;
var toReturn = $"👤 __`Name:`__ **{user.Username}#{user.Discriminator}**\n";
if (!string.IsNullOrWhiteSpace(user.Nickname))
toReturn += $"🆕 __`Nickname:`__ **{user.Nickname}** ";
toReturn += $@"🏷 __`ID:`__ **{user.Id}**
🎮 __`Current Game:`__ **{(user.Game?.Name == null ? "-" : user.Game.Name)}**
📅 __`Joined Server:`__ **{user.JoinedAt?.ToString("dd.MM.yyyy HH:mm")}**
🗓 __`Joined Discord:`__ **{user.CreatedAt.ToString("dd.MM.yyyy HH:mm")}**
__`Roles:`__ **({user.Roles.Count()}) - {string.Join(", ", user.Roles.Select(r => r.Name)).SanitizeMentions()}**";
if (!string.IsNullOrWhiteSpace(user.AvatarUrl))
toReturn += $@"
📷 __`Avatar URL:`__ **{await NadekoBot.Google.ShortenUrl(user.AvatarUrl).ConfigureAwait(false)}**";
await msg.Reply(toReturn).ConfigureAwait(false);
var embed = new EmbedBuilder()
.AddField(fb => fb.WithName("**Name**").WithValue($"**{user.Username}**#{user.Discriminator}").WithIsInline(true));
if (!string.IsNullOrWhiteSpace(user.Nickname)) {
embed.AddField(fb => fb.WithName("**Nickname**").WithValue(user.Nickname).WithIsInline(true));
}
embed.AddField(fb => fb.WithName("**ID**").WithValue(user.Id.ToString()).WithIsInline(true))
.AddField(fb => fb.WithName("**Joined Server**").WithValue($"{user.JoinedAt?.ToString("dd.MM.yyyy HH:mm")}").WithIsInline(true))
.AddField(fb => fb.WithName("**Joined Discord**").WithValue($"{user.CreatedAt.ToString("dd.MM.yyyy HH:mm")}").WithIsInline(true))
.AddField(fb => fb.WithName("**Current Game**").WithValue($"{(user.Game?.Name == null ? "-" : user.Game.Name)}").WithIsInline(true))
.AddField(fb => fb.WithName("**Roles**").WithValue($"**({user.Roles.Count()})** - {string.Join(", ", user.Roles.Select(r => r.Name)).SanitizeMentions()}").WithIsInline(true))
.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;
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())
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);
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]
@ -83,7 +83,7 @@ namespace NadekoBot.Modules.Utility
});
await uow.CompleteAsync().ConfigureAwait(false);
}
await channel.SendMessageAsync("✅ **Quote added.**").ConfigureAwait(false);
await channel.SendConfirmAsync("✅ Quote added.").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
@ -105,7 +105,7 @@ namespace NadekoBot.Modules.Utility
if (qs==null || !qs.Any())
{
response = " **No quotes found.**";
await channel.SendErrorAsync("No quotes found.");
return;
}
@ -115,7 +115,7 @@ namespace NadekoBot.Modules.Utility
await uow.CompleteAsync().ConfigureAwait(false);
response = "🗑 **Deleted a random quote.**";
}
await channel.SendMessageAsync(response);
await channel.SendConfirmAsync(response);
}
[NadekoCommand, Usage, Description, Aliases]
@ -139,7 +139,7 @@ namespace NadekoBot.Modules.Utility
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)
{
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;
}
@ -132,7 +132,7 @@ namespace NadekoBot.Modules.Utility
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;
}
@ -157,7 +157,7 @@ namespace NadekoBot.Modules.Utility
(groupName == "hours" && value > 23) ||
(groupName == "minutes" && value > 59))
{
await channel.SendMessageAsync($"⚠️ Invalid {groupName} value.").ConfigureAwait(false);
await channel.SendErrorAsync($"Invalid {groupName} value.").ConfigureAwait(false);
return;
}
else
@ -187,7 +187,7 @@ namespace NadekoBot.Modules.Utility
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);
}
@ -206,7 +206,7 @@ namespace NadekoBot.Modules.Utility
uow.BotConfig.GetOrCreate().RemindMessageFormat = arg.Trim();
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>();
private static Logger _log;
private static Logger _log { get; }
private static Timer _timer;
private static TimeSpan updateInterval = new TimeSpan(12, 0, 0);
@ -104,15 +104,14 @@ namespace NadekoBot.Modules.Utility
[RequireContext(ContextType.Guild)]
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);
foreach (var group in res)
{
sb.AppendLine($"{group.Key}: ```xl");
sb.AppendLine(string.Join(",", group.Select(x => x.Triggers.FirstOrDefault()).OrderBy(x => x)));
sb.AppendLine("```");
}
await msg.ReplyLong(sb.ToString(), breakOn: new[] { "```xl\n", "\n" });
var res = Units.GroupBy(x => x.UnitType)
.Aggregate(new EmbedBuilder().WithTitle("__Units which can be used by the converter__")
.WithOkColor(),
(embed, g) => embed.AddField(efb =>
efb.WithName(g.Key.ToTitleCase())
.WithValue(String.Join(", ", g.Select(x => x.Triggers.FirstOrDefault())
.OrderBy(x => x)))));
await msg.Channel.EmbedAsync(res.Build());
}
[NadekoCommand, Usage, Description, Aliases]
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()));
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;
}
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;
}
decimal res;
@ -170,7 +169,7 @@ namespace NadekoBot.Modules.Utility
}
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 EmbedAuthor = Discord.API.EmbedAuthor;
using EmbedField = Discord.API.EmbedField;
using System.Net.Http;
namespace NadekoBot.Modules.Utility
{
@ -22,11 +23,32 @@ namespace NadekoBot.Modules.Utility
[NadekoModule("Utility", ".")]
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]
[RequireContext(ContextType.Guild)]
public async Task WhosPlaying(IUserMessage umsg, [Remainder] string game = null)
@ -35,16 +57,18 @@ namespace NadekoBot.Modules.Utility
game = game.Trim().ToUpperInvariant();
if (string.IsNullOrWhiteSpace(game))
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)
.Select(u => u.Username)
.ToList();
int i = 0;
if (!arr.Any())
if (!usrs.Any())
await channel.SendErrorAsync("Nobody is playing that game.").ConfigureAwait(false);
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]
@ -55,7 +79,7 @@ namespace NadekoBot.Modules.Utility
return;
var channel = (ITextChannel)umsg.Channel;
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"))
{
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)
{
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;
}
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);
send = curstr.Substring(curstr.LastIndexOf(", ", StringComparison.Ordinal) + 1) +
send.Substring(2000);
}
await channel.SendMessageAsync(send).ConfigureAwait(false);
await channel.SendConfirmAsync(send).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
@ -95,7 +119,7 @@ namespace NadekoBot.Modules.Utility
}
builder.Append("```");
await msg.Reply(builder.ToString());
await msg.Channel.SendConfirmAsync(builder.ToString());
}
[NadekoCommand, Usage, Description, Aliases]
@ -103,20 +127,20 @@ namespace NadekoBot.Modules.Utility
public async Task UserId(IUserMessage msg, IGuildUser target = null)
{
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]
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]
[RequireContext(ContextType.Guild)]
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]
@ -130,13 +154,30 @@ namespace NadekoBot.Modules.Utility
if (page < 1 || page > 100)
return;
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
{
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;
if (string.IsNullOrWhiteSpace(topic))
await channel.SendMessageAsync("❎ **No topic set.**");
await channel.SendErrorAsync("No topic set.");
else
await channel.SendMessageAsync(" **Topic:** " + topic);
await channel.SendConfirmAsync("Channel topic", topic);
}
[NadekoCommand, Usage, Description, Aliases]
@ -166,64 +207,23 @@ namespace NadekoBot.Modules.Utility
var stats = NadekoBot.Stats;
await channel.EmbedAsync(
new Embed()
{
Author = new EmbedAuthor()
{
Name = $"NadekoBot v{StatsService.BotVersion}",
Url = "http://nadekobot.readthedocs.io/en/latest/",
IconUrl = "https://cdn.discordapp.com/avatars/116275390695079945/b21045e778ef21c96d175400e779f0fb.jpg"
},
Fields = new[] {
new EmbedField() {
Name = "Author",
Value = stats.Author,
Inline = true
},
new EmbedField() {
Name = "Library",
Value = stats.Library,
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
});
new EmbedBuilder().WithOkColor()
.WithAuthor(eab => eab.WithName($"NadekoBot v{StatsService.BotVersion}")
.WithUrl("http://nadekobot.readthedocs.io/en/latest/")
.WithIconUrl("https://cdn.discordapp.com/avatars/116275390695079945/b21045e778ef21c96d175400e779f0fb.jpg"))
.AddField(efb => efb.WithName(Format.Bold("Author")).WithValue(stats.Author).WithIsInline(true))
.AddField(efb => efb.WithName(Format.Bold("Library")).WithValue(stats.Library).WithIsInline(true))
.AddField(efb => efb.WithName(Format.Bold("Bot ID")).WithValue(NadekoBot.Client.GetCurrentUser().Id.ToString()).WithIsInline(true))
.AddField(efb => efb.WithName(Format.Bold("Commands Ran")).WithValue(stats.CommandsRan.ToString()).WithIsInline(true))
.AddField(efb => efb.WithName(Format.Bold("Messages")).WithValue($"{stats.MessageCounter} ({stats.MessagesPerSecond:F2}/sec)").WithIsInline(true))
.AddField(efb => efb.WithName(Format.Bold("Memory")).WithValue($"{stats.Heap} MB").WithIsInline(true))
.AddField(efb => efb.WithName(Format.Bold("Owner ID(s)")).WithValue(stats.OwnerIds).WithIsInline(true))
.AddField(efb => efb.WithName(Format.Bold("Uptime")).WithValue(stats.GetUptimeString("\n")).WithIsInline(true))
.AddField(efb => efb.WithName(Format.Bold("Presence")).WithValue($"{NadekoBot.Client.GetGuilds().Count} Servers\n{stats.TextChannels} Text Channels\n{stats.VoiceChannels} Voice Channels").WithIsInline(true))
#if !GLOBAL_NADEKO
.WithFooter(efb => efb.WithText($"Playing {Music.Music.MusicPlayers.Where(mp => mp.Value.CurrentSong != null).Count()} songs, {Music.Music.MusicPlayers.Sum(mp => mp.Value.Playlist.Count)} queued."))
#endif
.Build());
}
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>()
.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);
}
@ -254,43 +257,16 @@ namespace NadekoBot.Modules.Utility
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;
}
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} ```")
.WithIsInline(false)))
.Build())
.ConfigureAwait(false);
}
//[NadekoCommand, Usage, Description, Aliases]
//[RequireContext(ContextType.Guild)]
//public async Task TextToImage(IUserMessage msg, [Remainder] string arg)
//{
// 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 NadekoBot.Modules.Music;
using NadekoBot.Services.Database.Models;
using NadekoBot.Modules.Games.Commands.Hangman;
namespace NadekoBot
{
@ -24,13 +25,12 @@ namespace NadekoBot
{
private Logger _log;
public static uint OkColor { get; } = 0x00ff00;
public static uint ErrorColor { get; } = 0xff0000;
public static uint OkColor { get; } = 0x71cd40;
public static uint ErrorColor { get; } = 0xee281f;
public static CommandService CommandService { get; private set; }
public static CommandHandler CommandHandler { 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 GoogleApiService Google { get; private set; }
@ -48,7 +48,7 @@ namespace NadekoBot
using (var uow = DbHandler.UnitOfWork())
{
AllGuildConfigs = uow.GuildConfigs.GetAll();
AllGuildConfigs = uow.GuildConfigs.GetAllGuildConfigs();
}
}
@ -70,17 +70,16 @@ namespace NadekoBot
//initialize Services
CommandService = new CommandService();
Localizer = new Localization();
Google = new GoogleApiService();
CommandHandler = new CommandHandler(Client, CommandService);
Stats = new StatsService(Client, CommandHandler);
//setup DI
var depMap = new DependencyMap();
depMap.Add<ILocalization>(Localizer);
depMap.Add<ShardedDiscordClient>(Client);
depMap.Add<CommandService>(CommandService);
depMap.Add<IGoogleApiService>(Google);
////setup DI
//var depMap = new DependencyMap();
//depMap.Add<ILocalization>(Localizer);
//depMap.Add<ShardedDiscordClient>(Client);
//depMap.Add<CommandService>(CommandService);
//depMap.Add<IGoogleApiService>(Google);
//setup typereaders
@ -104,9 +103,9 @@ namespace NadekoBot
// start handling messages received in commandhandler
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
await CommandService.Load(new Music(Localizer, CommandService, Client, Google)).ConfigureAwait(false);
await CommandService.Load(new Music()).ConfigureAwait(false);
#endif
Ready = true;
Console.WriteLine(await Stats.Print().ConfigureAwait(false));

View File

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

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