Merge pull request #9 from Kwoth/dev

update
This commit is contained in:
miraai 2017-01-09 20:32:36 +01:00 committed by GitHub
commit 1de7e24f5a
157 changed files with 20059 additions and 28938 deletions

8
.gitmodules vendored
View File

@ -1,4 +1,4 @@
[submodule "discord.net"]
path = discord.net
url = https://github.com/kwoth/discord.net
branch = dev
[submodule "Discord.Net"]
path = Discord.Net
url = https://github.com/Kwoth/Discord.Net
branch = rogue-dev

1
Discord.Net Submodule

@ -0,0 +1 @@
Subproject commit b9f767337d2b7c07ed76eb83c3bc5030109d5238

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

@ -12,10 +12,14 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "NadekoBot", "src\NadekoBot\NadekoBot.xproj", "{45EC1473-C678-4857-A544-07DFE0D0B478}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Discord.Net", "discord.net\src\Discord.Net\Discord.Net.xproj", "{91E9E7BD-75C9-4E98-84AA-2C271922E5C2}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Discord.Net.Commands", "discord.net\src\Discord.Net.Commands\Discord.Net.Commands.xproj", "{078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Discord.Net.Core", "discord.net\src\Discord.Net.Core\Discord.Net.Core.xproj", "{E5F4786F-58F3-469E-8C87-1908A95436B7}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Discord.Net.Rest", "discord.net\src\Discord.Net.Rest\Discord.Net.Rest.xproj", "{63F5B5C8-56FE-4B53-8003-B58CEB451EF9}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Discord.Net.WebSocket", "discord.net\src\Discord.Net.WebSocket\Discord.Net.WebSocket.xproj", "{E9800F7A-3354-41B1-BDBB-2D59F8124EC9}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -25,22 +29,34 @@ Global
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{45EC1473-C678-4857-A544-07DFE0D0B478}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{45EC1473-C678-4857-A544-07DFE0D0B478}.Debug|Any CPU.Build.0 = Debug|Any CPU
{45EC1473-C678-4857-A544-07DFE0D0B478}.GlobalNadeko|Any CPU.ActiveCfg = GlobalNadeko|Any CPU
{45EC1473-C678-4857-A544-07DFE0D0B478}.GlobalNadeko|Any CPU.Build.0 = GlobalNadeko|Any CPU
{45EC1473-C678-4857-A544-07DFE0D0B478}.GlobalNadeko|Any CPU.ActiveCfg = Release|Any CPU
{45EC1473-C678-4857-A544-07DFE0D0B478}.GlobalNadeko|Any CPU.Build.0 = Release|Any CPU
{45EC1473-C678-4857-A544-07DFE0D0B478}.Release|Any CPU.ActiveCfg = Release|Any CPU
{45EC1473-C678-4857-A544-07DFE0D0B478}.Release|Any CPU.Build.0 = Release|Any CPU
{91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.GlobalNadeko|Any CPU.ActiveCfg = GlobalNadeko|Any CPU
{91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.GlobalNadeko|Any CPU.Build.0 = GlobalNadeko|Any CPU
{91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Release|Any CPU.Build.0 = Release|Any CPU
{078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.GlobalNadeko|Any CPU.ActiveCfg = GlobalNadeko|Any CPU
{078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.GlobalNadeko|Any CPU.Build.0 = GlobalNadeko|Any CPU
{078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.GlobalNadeko|Any CPU.ActiveCfg = Debug|Any CPU
{078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.GlobalNadeko|Any CPU.Build.0 = Debug|Any CPU
{078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Release|Any CPU.Build.0 = Release|Any CPU
{E5F4786F-58F3-469E-8C87-1908A95436B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E5F4786F-58F3-469E-8C87-1908A95436B7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E5F4786F-58F3-469E-8C87-1908A95436B7}.GlobalNadeko|Any CPU.ActiveCfg = Debug|Any CPU
{E5F4786F-58F3-469E-8C87-1908A95436B7}.GlobalNadeko|Any CPU.Build.0 = Debug|Any CPU
{E5F4786F-58F3-469E-8C87-1908A95436B7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E5F4786F-58F3-469E-8C87-1908A95436B7}.Release|Any CPU.Build.0 = Release|Any CPU
{63F5B5C8-56FE-4B53-8003-B58CEB451EF9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{63F5B5C8-56FE-4B53-8003-B58CEB451EF9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{63F5B5C8-56FE-4B53-8003-B58CEB451EF9}.GlobalNadeko|Any CPU.ActiveCfg = Debug|Any CPU
{63F5B5C8-56FE-4B53-8003-B58CEB451EF9}.GlobalNadeko|Any CPU.Build.0 = Debug|Any CPU
{63F5B5C8-56FE-4B53-8003-B58CEB451EF9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{63F5B5C8-56FE-4B53-8003-B58CEB451EF9}.Release|Any CPU.Build.0 = Release|Any CPU
{E9800F7A-3354-41B1-BDBB-2D59F8124EC9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E9800F7A-3354-41B1-BDBB-2D59F8124EC9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E9800F7A-3354-41B1-BDBB-2D59F8124EC9}.GlobalNadeko|Any CPU.ActiveCfg = Debug|Any CPU
{E9800F7A-3354-41B1-BDBB-2D59F8124EC9}.GlobalNadeko|Any CPU.Build.0 = Debug|Any CPU
{E9800F7A-3354-41B1-BDBB-2D59F8124EC9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E9800F7A-3354-41B1-BDBB-2D59F8124EC9}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

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 +0,0 @@
Subproject commit 48b66d55f3f26f60fc001123ff6d7afeade3a51d

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)
@ -17,8 +18,60 @@ You can support the project on patreon: <https://patreon.com/nadekobot> or paypa
### Administration
Command and aliases | Description | Usage
----------------|--------------|-------
`.resetperms` | Resets BOT's permissions module on this server to the default value. **Requires Administrator server permission.** | `.resetperms`
`.delmsgoncmd` | Toggles the automatic deletion of user's successful command message to prevent chat flood. **Requires Administrator server permission.** | `.delmsgoncmd`
`.voice+text` `.v+t` | Creates a text channel for each voice channel only users in that voice channel can see.If you are server owner, keep in mind you will see them all the time regardless. **Requires ManageRoles server permission.** **Requires ManageChannels server permission.** | `.voice+text`
`.cleanvplust` `.cv+t` | Deletes all text channels ending in `-voice` for which voicechannels are not found. Use at your own risk. **Requires ManageChannels server permission.** **Requires ManageRoles server permission.** | `.cleanv+t`
`.greetdel` `.grdel` | Sets the time it takes (in seconds) for greet messages to be auto-deleted. Set 0 to disable automatic deletion. **Requires ManageServer server permission.** | `.greetdel 0` or `.greetdel 30`
`.greet` | Toggles anouncements on the current channel when someone joins the server. **Requires ManageServer server permission.** | `.greet`
`.greetmsg` | Sets a new join announcement message which will be shown in the server's channel. Type %user% if you want to mention the new member. Using it with no message will show the current greet message. **Requires ManageServer server permission.** | `.greetmsg Welcome, %user%.`
`.greetdm` | Toggles whether the greet messages will be sent in a DM (This is separate from greet - you can have both, any or neither enabled). **Requires ManageServer server permission.** | `.greetdm`
`.greetdmmsg` | Sets a new join announcement message which will be sent to the user who joined. Type %user% if you want to mention the new member. Using it with no message will show the current DM greet message. **Requires ManageServer server permission.** | `.greetdmmsg Welcome to the server, %user%`.
`.bye` | Toggles anouncements on the current channel when someone leaves the server. **Requires ManageServer server permission.** | `.bye`
`.byemsg` | Sets a new leave announcement message. Type %user% if you want to show the name the user who left. Type %id% to show id. Using this command with no message will show the current bye message. **Requires ManageServer server permission.** | `.byemsg %user% has left.`
`.byedel` | Sets the time it takes (in seconds) for bye messages to be auto-deleted. Set 0 to disable automatic deletion. **Requires ManageServer server permission.** | `.byedel 0` or `.byedel 30`
`.leave` | Makes Nadeko leave the server. Either name or id required. **Bot Owner only.** | `.leave 123123123331`
`.die` | Shuts the bot down. **Bot Owner only.** | `.die`
`.setname` `.newnm` | Gives the bot a new name. **Bot Owner only.** | `.newnm BotName`
`.setavatar` `.setav` | Sets a new avatar image for the NadekoBot. Argument is a direct link to an image. **Bot Owner only.** | `.setav http://i.imgur.com/xTG3a1I.jpg`
`.setgame` | Sets the bots game. **Bot Owner only.** | `.setgame with snakes`
`.setstream` | Sets the bots stream. First argument is the twitch link, second argument is stream name. **Bot Owner only.** | `.setstream TWITCHLINK Hello`
`.send` | Sends a message to someone on a different server through the bot. Separate server and channel/user ids with `|` and prepend channel id with `c:` and user id with `u:`. **Bot Owner only.** | `.send serverid|c:channelid message` or `.send serverid|u:userid message`
`.announce` | Sends a message to all servers' general channel bot is connected to. **Bot Owner only.** | `.announce Useless spam`
`.adsarm` | Toggles the automatic deletion of confirmations for .iam and .iamn commands. **Requires ManageMessages server permission.** | `.adsarm`
`.asar` | Adds a role to the list of self-assignable roles. **Requires ManageRoles server permission.** | `.asar Gamer`
`.rsar` | Removes a specified role from the list of self-assignable roles. **Requires ManageRoles server permission.** | `.rsar`
`.lsar` | Lists all self-assignable roles. | `.lsar`
`.togglexclsar` `.tesar` | Toggles whether the self-assigned roles are exclusive. (So that any person can have only one of the self assignable roles) **Requires ManageRoles server permission.** | `.tesar`
`.iam` | Adds a role to you that you choose. Role must be on a list of self-assignable roles. | `.iam Gamer`
`.iamnot` `.iamn` | Removes a role to you that you choose. Role must be on a list of self-assignable roles. | `.iamn Gamer`
`.slowmode` | Toggles slowmode. Disable by specifying no parameters. To enable, specify a number of messages each user can send, and an interval in seconds. For example 1 message every 5 seconds. **Requires ManageMessages server permission.** | `.slowmode 1 5` or `.slowmode`
`.rotateplaying` `.ropl` | Toggles rotation of playing status of the dynamic strings you previously specified. **Bot Owner only.** | `.ropl`
`.addplaying` `.adpl` | Adds a specified string to the list of playing strings to rotate. Supported placeholders: %servers%, %users%, %playing%, %queued% **Bot Owner only.** | `.adpl`
`.listplaying` `.lipl` | Lists all playing statuses with their corresponding number. **Bot Owner only.** | `.lipl`
`.removeplaying` `.rmpl` `.repl` | Removes a playing string on a given number. **Bot Owner only.** | `.rmpl`
`.setmuterole` | Sets a name of the role which will be assigned to people who should be muted. Default is nadeko-mute. **Requires ManageRoles server permission.** | `.setmuterole Silenced`
`.mute` | Mutes a mentioned user both from speaking and chatting. **Requires ManageRoles server permission.** **Requires MuteMembers server permission.** | `.mute @Someone`
`.unmute` | Unmutes a mentioned user previously muted with `.mute` command. **Requires ManageRoles server permission.** **Requires MuteMembers server permission.** | `.unmute @Someone`
`.chatmute` | Prevents a mentioned user from chatting in text channels. **Requires ManageRoles server permission.** | `.chatmute @Someone`
`.chatunmute` | Removes a mute role previously set on a mentioned user with `.chatmute` which prevented him from chatting in text channels. **Requires ManageRoles server permission.** | `.chatunmute @Someone`
`.voicemute` | Prevents a mentioned user from speaking in voice channels. **Requires MuteMembers server permission.** | `.voicemute @Someone`
`.voiceunmute` | Gives a previously voice-muted user a permission to speak. **Requires MuteMembers server permission.** | `.voiceunmute @Someguy`
`.migratedata` | Migrate data from old bot configuration **Bot Owner only.** | `.migratedata`
`.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`
`.logserver` | Enables or Disables ALL log events. If enabled, all log events will log to this channel. **Requires Administrator server permission.** **Bot Owner only.** | `.logserver enable` or `.logserver disable`
`.logignore` | Toggles whether the .logserver command ignores this channel. Useful if you have hidden admin channel and public log channel. **Requires Administrator server permission.** **Bot Owner only.** | `.logignore`
`.logevents` | Shows a list of all events you can subscribe to with `.log` **Requires Administrator server permission.** **Bot Owner only.** | `.logevents`
`.log` | Toggles logging event. Disables it if it's active anywhere on the server. Enables if it's not active. Use `.logevents` to see a list of all events you can subscribe to. **Requires Administrator server permission.** **Bot Owner only.** | `.log userpresence` or `.log userbanned`
`.fwmsgs` | Toggles forwarding of non-command messages sent to bot's DM to the bot owners **Bot Owner only.** | `.fwmsgs`
`.fwtoall` | Toggles whether messages will be forwarded to all bot owners or only to the first one specified in the credentials.json **Bot Owner only.** | `.fwtoall`
`.scsc` | Starts an instance of cross server channel. You will get a token as a DM that other people will use to tune in to the same instance. **Bot Owner only.** | `.scsc`
`.jcsc` | Joins current channel to an instance of cross server channel using the token. **Requires ManageServer server permission.** | `.jcsc TokenHere`
`.lcsc` | Leaves Cross server channel instance from this channel. **Requires ManageServer server permission.** | `.lcsc`
`.autoassignrole` `.aar` | Automaticaly assigns a specified role to every user who joins the server. **Requires ManageRoles server permission.** | `.aar` to disable, `.aar Role Name` to enable
`.antiraid` | Sets an anti-raid protection on the server. First argument is number of people which will trigger the protection. Second one is a time interval in which that number of people needs to join in order to trigger the protection, and third argument is punishment for those people (Kick, Ban, Mute) **Requires Administrator server permission.** | `.antiraid 5 20 Kick`
`.antispam` | Stops people from repeating same message X times in a row. You can specify to either mute, kick or ban the offenders. **Requires Administrator server permission.** | `.antispam 3 Mute` or `.antispam 4 Kick` or `.antispam 6 Ban`
`.resetperms` | Resets BOT's permissions module on this server to the default value. **Requires Administrator server permission.** | `.resetperms`
`.delmsgoncmd` | Toggles the automatic deletion of user's successful command message to prevent chat flood. **Requires Administrator server permission.** | `.delmsgoncmd`
`.setrole` `.sr` | Sets a role for a given user. **Requires ManageRoles server permission.** | `.sr @User Guest`
`.removerole` `.rr` | Removes a role from a given user. **Requires ManageRoles server permission.** | `.rr @User Admin`
`.renamerole` `.renr` | Renames a role. Roles you are renaming must be lower than bot's highest role. **Requires ManageRoles server permission.** | `.renr "First role" SecondRole`
@ -28,13 +81,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`
@ -44,55 +90,10 @@ Command and aliases | Description | Usage
`.settopic` `.st` | Sets a topic on the current channel. **Requires ManageChannels server permission.** | `.st My new topic`
`.setchanlname` `.schn` | Changes the name of the current channel. **Requires ManageChannels server permission.** | `.schn NewName`
`.prune` `.clr` | `.prune` removes all nadeko's messages in the last 100 messages.`.prune X` removes last X messages from the channel (up to 100)`.prune @Someone` removes all Someone's messages in the last 100 messages.`.prune @Someone X` removes last X 'Someone's' messages in the channel. | `.prune` or `.prune 5` or `.prune @Someone` or `.prune @Someone X`
`.die` | Shuts the bot down. **Bot Owner only.** | `.die`
`.setname` `.newnm` | Gives the bot a new name. **Bot Owner only.** | `.newnm BotName`
`.setavatar` `.setav` | Sets a new avatar image for the NadekoBot. Argument is a direct link to an image. **Bot Owner only.** | `.setav http://i.imgur.com/xTG3a1I.jpg`
`.setgame` | Sets the bots game. **Bot Owner only.** | `.setgame with snakes`
`.setstream` | Sets the bots stream. First argument is the twitch link, second argument is stream name. **Bot Owner only.** | `.setstream TWITCHLINK Hello`
`.send` | Sends a message to someone on a different server through the bot. Separate server and channel/user ids with `|` and prepend channel id with `c:` and user id with `u:`. **Bot Owner only.** | `.send serverid|c:channelid message` or `.send serverid|u:userid message`
`.announce` | Sends a message to all servers' general channel bot is connected to. **Bot Owner only.** | `.announce Useless spam`
`.savechat` | Saves a number of messages to a text file and sends it to you. **Bot Owner only.** | `.savechat 150`
`.savechat` | Saves a number of messages to a text file and sends it to you. **Bot Owner only.** | `.savechat 150`
`.mentionrole` `.menro` | Mentions every person from the provided role or roles (separated by a ',') on this server. Requires you to have mention everyone permission. **Requires MentionEveryone server permission.** | `.menro RoleName`
`.donators` | List of lovely people who donated to keep this project alive. | `.donators`
`.donadd` | Add a donator to the database. **Bot Owner only.** | `.donadd Donate Amount`
`.antiraid` | Sets an anti-raid protection on the server. First argument is number of people which will trigger the protection. Second one is a time interval in which that number of people needs to join in order to trigger the protection, and third argument is punishment for those people (Kick, Ban, Mute) **Requires Administrator server permission.** | `.antiraid 5 20 Kick`
`.antispam` | Stops people from repeating same message X times in a row. You can specify to either mute, kick or ban the offenders. **Requires Administrator server permission.** | `.antispam 3 Mute` or `.antispam 4 Kick` or `.antispam 6 Ban`
`.autoassignrole` `.aar` | Automaticaly assigns a specified role to every user who joins the server. **Requires ManageRoles server permission.** | `.aar` to disable, `.aar Role Name` to enable
`.scsc` | Starts an instance of cross server channel. You will get a token as a DM that other people will use to tune in to the same instance. **Bot Owner only.** | `.scsc`
`.jcsc` | Joins current channel to an instance of cross server channel using the token. **Requires ManageServer server permission.** | `.jcsc TokenHere`
`.lcsc` | Leaves Cross server channel instance from this channel. **Requires ManageServer server permission.** | `.lcsc`
`.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`
`.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`
`.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`
`.rotateplaying` `.ropl` | Toggles rotation of playing status of the dynamic strings you previously specified. **Bot Owner only.** | `.ropl`
`.addplaying` `.adpl` | Adds a specified string to the list of playing strings to rotate. Supported placeholders: %servers%, %users%, %playing%, %queued% **Bot Owner only.** | `.adpl`
`.listplaying` `.lipl` | Lists all playing statuses with their corresponding number. **Bot Owner only.** | `.lipl`
`.removeplaying` `.rmpl` `.repl` | Removes a playing string on a given number. **Bot Owner only.** | `.rmpl`
`.slowmode` | Toggles slowmode. Disable by specifying no parameters. To enable, specify a number of messages each user can send, and an interval in seconds. For example 1 message every 5 seconds. **Requires ManageMessages server permission.** | `.slowmode 1 5` or `.slowmode`
`.adsarm` | Toggles the automatic deletion of confirmations for .iam and .iamn commands. **Requires ManageMessages server permission.** | `.adsarm`
`.asar` | Adds a role to the list of self-assignable roles. **Requires ManageRoles server permission.** | `.asar Gamer`
`.rsar` | Removes a specified role from the list of self-assignable roles. **Requires ManageRoles server permission.** | `.rsar`
`.lsar` | Lists all self-assignable roles. | `.lsar`
`.togglexclsar` `.tesar` | Toggles whether the self-assigned roles are exclusive. (So that any person can have only one of the self assignable roles) **Requires ManageRoles server permission.** | `.tesar`
`.iam` | Adds a role to you that you choose. Role must be on a list of self-assignable roles. | `.iam Gamer`
`.iamnot` `.iamn` | Removes a role to you that you choose. Role must be on a list of self-assignable roles. | `.iamn Gamer`
`.leave` | Makes Nadeko leave the server. Either name or id required. **Bot Owner only.** | `.leave 123123123331`
`.greetdel` `.grdel` | Sets the time it takes (in seconds) for greet messages to be auto-deleted. Set 0 to disable automatic deletion. **Requires ManageServer server permission.** | `.greetdel 0` or `.greetdel 30`
`.greet` | Toggles anouncements on the current channel when someone joins the server. **Requires ManageServer server permission.** | `.greet`
`.greetmsg` | Sets a new join announcement message which will be shown in the server's channel. Type %user% if you want to mention the new member. Using it with no message will show the current greet message. **Requires ManageServer server permission.** | `.greetmsg Welcome, %user%.`
`.greetdm` | Toggles whether the greet messages will be sent in a DM (This is separate from greet - you can have both, any or neither enabled). **Requires ManageServer server permission.** | `.greetdm`
`.greetdmmsg` | Sets a new join announcement message which will be sent to the user who joined. Type %user% if you want to mention the new member. Using it with no message will show the current DM greet message. **Requires ManageServer server permission.** | `.greetdmmsg Welcome to the server, %user%`.
`.bye` | Toggles anouncements on the current channel when someone leaves the server. **Requires ManageServer server permission.** | `.bye`
`.byemsg` | Sets a new leave announcement message. Type %user% if you want to mention the new member. Using it 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`
`.donators` | List of lovely people who donated to keep this project alive. | `.donators`
`.donadd` | Add a donator to the database. **Bot Owner only.** | `.donadd Donate Amount`
###### [Back to TOC](#table-of-contents)
@ -119,54 +120,59 @@ 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. **Bot Owner only.** | `.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)
### Gambling
Command and aliases | Description | Usage
----------------|--------------|-------
`$raffle` | Prints a name and ID of a random user from the online list from the (optional) role. | `$raffle` or `$raffle RoleName`
`$flip` | Flips coin(s) - heads or tails, and shows an image. | `$flip` or `$flip 3`
`$betflip` `$bf` | Bet to guess will the result be heads or tails. Guessing awards you 1.8x the currency you've bet. | `$bf 5 heads` or `$bf 3 t`
`$draw` | Draws a card from the deck.If you supply number X, she draws up to 5 cards from the deck. | `$draw` or `$draw 5`
`$shuffle` `$sh` | Reshuffles all cards back into the deck. | `$sh`
`$roll` | Rolls 0-100. If you supply a number [x] it rolls up to 30 normal dice. If you split 2 numbers with letter d (xdy) it will roll x dice from 1 to y. Y can be a letter 'F' if you want to roll fate dice instead of dnd. | `$roll` or `$roll 7` or `$roll 3d5` or `$roll 5dF`
`$rolluo` | Rolls X normal dice (up to 30) unordered. If you split 2 numbers with letter d (xdy) it will roll x dice from 1 to y. | `$rolluo` or `$rolluo 7` or `$rolluo 3d5`
`$nroll` | Rolls in a given range. | `$nroll 5` (rolls 0-5) or `$nroll 5-15`
`$race` | Starts a new animal race. | `$race`
`$joinrace` `$jr` | Joins a new race. You can specify an amount of currency for betting (optional). You will get YourBet*(participants-1) back if you win. | `$jr` or `$jr 5`
`$raffle` | Prints a name and ID of a random user from the online list from the (optional) role. | `$raffle` or `$raffle RoleName`
`$cash` `$$$` | Check how much currency a person has. (Defaults to yourself) | `$$$` or `$$$ @SomeGuy`
`$give` | Give someone a certain amount of currency. | `$give 1 "@SomeGuy"`
`$award` | Awards someone a certain amount of currency. You can also specify a role name to award currency to all users in a role. **Bot Owner only.** | `$award 100 @person` or `$award 5 Role Of Gamblers`
`$take` | Takes a certain amount of currency from someone. **Bot Owner only.** | `$take 1 "@someguy"`
`$give` | Give someone a certain amount of currency. | `$give 1 "@SomeGuy"`
`$award` | Awards someone a certain amount of currency. You can also specify a role name to award currency to all users in a role. **Bot Owner only.** | `$award 100 @person` or `$award 5 Role Of Gamblers`
`$take` | Takes a certain amount of currency from someone. **Bot Owner only.** | `$take 1 "@someguy"`
`$betroll` `$br` | Bets a certain amount of currency and rolls a dice. Rolling over 66 yields x2 of your currency, over 90 - x3 and 100 x10. | `$br 5`
`$leaderboard` `$lb` | Displays bot currency leaderboard. | `$lb`
`$race` | Starts a new animal race. | `$race`
`$joinrace` `$jr` | Joins a new race. You can specify an amount of currency for betting (optional). You will get YourBet*(participants-1) back if you win. | `$jr` or `$jr 5`
`$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`
`$rolluo` | Rolls X normal dice (up to 30) unordered. If you split 2 numbers with letter d (xdy) it will roll x dice from 1 to y. | `$rolluo` or `$rolluo 7` or `$rolluo 3d5`
`$nroll` | Rolls in a given range. | `$nroll 5` (rolls 0-5) or `$nroll 5-15`
`$draw` | Draws a card from the deck.If you supply number X, she draws up to 5 cards from the deck. | `$draw` or `$draw 5`
`$shuffle` `$sh` | Reshuffles all cards back into the deck. | `$sh`
`$flip` | Flips coin(s) - heads or tails, and shows an image. | `$flip` or `$flip 3`
`$betflip` `$bf` | Bet to guess will the result be heads or tails. Guessing awards you 1.8x the currency you've bet. | `$bf 5 heads` or `$bf 3 t`
###### [Back to TOC](#table-of-contents)
### Games
Command and aliases | Description | Usage
----------------|--------------|-------
`>choose` | Chooses a thing from a list of things | `>choose Get up;Sleep;Sleep more`
`>8ball` | Ask the 8ball a yes/no question. | `>8ball should I do something`
`>rps` | Play a game of rocket paperclip scissors with Nadeko. | `>rps scissors`
`>linux` | Prints a customizable Linux interjection | `>linux Spyware Windows`
`>leet` | Converts a text to leetspeak with 6 (1-6) severity levels | `>leet 3 Hello`
`>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`
`>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`
`>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`
`>typestop` | Stops a typing contest on the current channel. | `>typestop`
`>typeadd` | Adds a new article to the typing contest. **Bot Owner only.** | `>typeadd wordswords`
`>typelist` | Lists added typing articles with their IDs. 15 per page. | `>typelist` or `>typelist 3`
`>typedel` | Deletes a typing article given the ID. **Bot Owner only.** | `>typedel 3`
`>trivia` `>t` | Starts a game of trivia. You can add nohint to prevent hints.First player to get to 10 points wins by default. You can specify a different number. 30 seconds per question. | `>t` or `>t 5 nohint`
`>tl` | Shows a current trivia leaderboard. | `>tl`
`>tq` | Quits current trivia after current question. | `>tq`
`>tl` | Shows a current trivia leaderboard. | `>tl`
`>tq` | Quits current trivia after current question. | `>tq`
`>typestart` | Starts a typing contest. | `>typestart`
`>typestop` | Stops a typing contest on the current channel. | `>typestop`
`>typeadd` | Adds a new article to the typing contest. **Bot Owner only.** | `>typeadd wordswords`
`>typelist` | Lists added typing articles with their IDs. 15 per page. | `>typelist` or `>typelist 3`
`>typedel` | Deletes a typing article given the ID. **Bot Owner only.** | `>typedel 3`
`>poll` | Creates a poll which requires users to send the number of the voting option to the bot. **Requires ManageMessages server permission.** | `>poll Question?;Answer1;Answ 2;A_3`
`>publicpoll` `>ppoll` | Creates a public poll which requires users to type a number of the voting option in the channel command is ran in. **Requires ManageMessages server permission.** | `>ppoll Question?;Answer1;Answ 2;A_3`
`>pollend` | Stops active poll on this server and prints the results in this channel. **Requires ManageMessages server permission.** | `>pollend`
`>pick` | Picks the currency planted in this channel. 60 seconds cooldown. | `>pick`
`>plant` | Spend a unit of currency to plant it in this channel. (If bot is restarted or crashes, the currency will be lost) | `>plant`
`>gencurrency` `>gc` | Toggles currency generation on this channel. Every posted message will have chance to spawn currency. Chance is specified by the Bot Owner. (default is 2%) **Requires ManageMessages server permission.** | `>gc`
`>hangmanlist` | Shows a list of hangman term types. | `> hangmanlist`
`>hangman` | Starts a game of hangman in the channel. Use `>hangmanlist` to see a list of available term types. Defaults to 'all'. | `>hangman` or `>hangman movies`
`>cleverbot` | Toggles cleverbot session. When enabled, the bot will reply to messages starting with bot mention in the server. Custom reactions starting with %mention% won't work if cleverbot is enabled. **Requires ManageMessages channel permission.** | `>cleverbot`
`>acrophobia` `>acro` | Starts an Acrophobia game. Second argment is optional round length in seconds. (default is 60) | `>acro` or `>acro 30`
`>choose` | Chooses a thing from a list of things | `>choose Get up;Sleep;Sleep more`
`>8ball` | Ask the 8ball a yes/no question. | `>8ball should I do something`
`>rps` | Play a game of rocket paperclip scissors with Nadeko. | `>rps scissors`
`>linux` | Prints a customizable Linux interjection | `>linux Spyware Windows`
`>leet` | Converts a text to leetspeak with 6 (1-6) severity levels | `>leet 3 Hello`
###### [Back to TOC](#table-of-contents)
@ -176,9 +182,9 @@ Command and aliases | Description | Usage
`-modules` `-mdls` | Lists all bot modules. | `-modules`
`-commands` `-cmds` | List all of the bot's commands from a certain module. You can either specify full, or only first few letters of the module name. | `-commands Administration` or `-cmds Admin`
`-help` `-h` | Either shows a help for a single command, or DMs you help link if no arguments are specified. | `-h !!q` or `-h`
`-hgit` | Generates the commandlist.md file. **Bot Owner only.** | `-hgit`
`-hgit` | Generates the commandlist.md file. **Bot Owner only.** | `-hgit`
`-readme` `-guide` | Sends a readme and a guide links to the channel. | `-readme` or `-guide`
`-donate` | Instructions for helping the project financially. | `-donate`
`-donate` | Instructions for helping the project financially. | `-donate`
###### [Back to TOC](#table-of-contents)
@ -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`
`!!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 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`
`!!goto` | Goes to a specific time in seconds in a song. | `!!goto 30`
`!!autoplay` `!!ap` | Toggles autoplay - When the song is finished, automatically queue a related youtube song. (Works only for youtube songs and when queue is empty) | `!!ap`
###### [Back to TOC](#table-of-contents)
@ -220,16 +227,17 @@ Command and aliases | Description | Usage
### NSFW
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`
`~konachan` | Shows a random hentai image from konachan with a given tag. Tag is optional but preferred. | `~konachan yuri`
`~gelbooru` | Shows a random hentai image from gelbooru with a given tag. Tag is optional but preferred. (multiple tags are appended with +) | `~gelbooru yuri+kissing`
`~rule34` | Shows a random image from rule34.xx with a given tag. Tag is optional but preferred. (multiple tags are appended with +) | `~rule34 yuri+kissing`
`~e621` | Shows a random hentai image from e621.net with a given tag. Tag is optional but preferred. Use spaces for multiple tags. | `~e621 yuri kissing`
`~cp` | We all know where this will lead you to. | `~cp`
`~boobs` | Real adult content. | `~boobs`
`~hentai` | Shows a hentai image from a random website (gelbooru or danbooru or konachan or atfbooru or yandere) with a given tag. Tag is optional but preferred. Only 1 tag allowed. | `~hentai yuri`
`~autohentai` | Posts a hentai every X seconds with a random tag from the provided tags. Use `|` to separate tags. 20 seconds minimum. Provide no arguments to disable. | `~autohentai 30 yuri|tail|long_hair` or `~autohentai`
`~hentaibomb` | Shows a total 5 images (from gelbooru, danbooru, konachan, yandere and atfbooru). Tag is optional but preferred. | `~hentaibomb yuri`
`~danbooru` | Shows a random hentai image from danbooru with a given tag. Tag is optional but preferred. (multiple tags are appended with +) | `~danbooru yuri+kissing`
`~yandere` | Shows a random image from yandere with a given tag. Tag is optional but preferred. (multiple tags are appended with +) | `~yandere tag1+tag2`
`~konachan` | Shows a random hentai image from konachan with a given tag. Tag is optional but preferred. | `~konachan yuri`
`~gelbooru` | Shows a random hentai image from gelbooru with a given tag. Tag is optional but preferred. (multiple tags are appended with +) | `~gelbooru yuri+kissing`
`~rule34` | Shows a random image from rule34.xx with a given tag. Tag is optional but preferred. (multiple tags are appended with +) | `~rule34 yuri+kissing`
`~e621` | Shows a random hentai image from e621.net with a given tag. Tag is optional but preferred. Use spaces for multiple tags. | `~e621 yuri kissing`
`~cp` | We all know where this will lead you to. | `~cp`
`~boobs` | Real adult content. | `~boobs`
`~butts` `~ass` `~butt` | Real adult content. | `~butts` or `~ass`
###### [Back to TOC](#table-of-contents)
@ -237,124 +245,137 @@ Command and aliases | Description | Usage
### Permissions
Command and aliases | Description | Usage
----------------|--------------|-------
`;srvrfilterinv` `;sfi` | Toggles automatic deleting of invites posted in the server. Does not affect Bot Owner. | `;sfi`
`;chnlfilterinv` `;cfi` | Toggles automatic deleting of invites posted in the channel. Does not negate the ;srvrfilterinv enabled setting. Does not affect Bot Owner. | `;cfi`
`;srvrfilterwords` `;sfw` | Toggles automatic deleting of messages containing forbidden words on the server. Does not affect Bot Owner. | `;sfw`
`;chnlfilterwords` `;cfw` | Toggles automatic deleting of messages containing banned words on the channel. Does not negate the ;srvrfilterwords enabled setting. Does not affect bot owner. | `;cfw`
`;fw` | Adds or removes (if it exists) a word from the list of filtered words. Use`;sfw` or `;cfw` to toggle filtering. | `;fw poop`
`;lstfilterwords` `;lfw` | Shows a list of filtered words. | `;lfw`
`;cmdcooldown` `;cmdcd` | Sets a cooldown per user for a command. Set to 0 to remove the cooldown. | `;cmdcd "some cmd" 5`
`;allcmdcooldowns` `;acmdcds` | Shows a list of all commands and their respective cooldowns. | `;acmdcds`
`;ubl` | Either [add]s or [rem]oves a user specified by a mention or ID from a blacklist. **Bot Owner only.** | `;ubl add @SomeUser` or `;ubl rem 12312312313`
`;cbl` | Either [add]s or [rem]oves a channel specified by an ID from a blacklist. **Bot Owner only.** | `;cbl rem 12312312312`
`;sbl` | Either [add]s or [rem]oves a server specified by a Name or ID from a blacklist. **Bot Owner only.** | `;sbl add 12312321312` or `;sbl rem SomeTrashServer`
`;verbose` `;v` | Sets whether to show when a command/module is blocked. | `;verbose true`
`;permrole` `;pr` | Sets a role which can change permissions. Or supply no parameters to find out the current one. Default one is 'Nadeko'. | `;pr role`
`;listperms` `;lp` | Lists whole permission chain with their indexes. You can specify an optional page number if there are a lot of permissions. | `;lp` or `;lp 3`
`;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`
`;allsrvrmdls` `;asm` | Enable or disable all modules for your server. | `;asm [enable/disable]`
`;ubl` | Either [add]s or [rem]oves a user specified by a mention or ID from a blacklist. **Bot Owner only.** | `;ubl add @SomeUser` or `;ubl rem 12312312313`
`;cbl` | Either [add]s or [rem]oves a channel specified by an ID from a blacklist. **Bot Owner only.** | `;cbl rem 12312312312`
`;sbl` | Either [add]s or [rem]oves a server specified by a Name or ID from a blacklist. **Bot Owner only.** | `;sbl add 12312321312` or `;sbl rem SomeTrashServer`
`;cmdcooldown` `;cmdcd` | Sets a cooldown per user for a command. Set to 0 to remove the cooldown. | `;cmdcd "some cmd" 5`
`;allcmdcooldowns` `;acmdcds` | Shows a list of all commands and their respective cooldowns. | `;acmdcds`
`;srvrfilterinv` `;sfi` | Toggles automatic deleting of invites posted in the server. Does not affect Bot Owner. | `;sfi`
`;chnlfilterinv` `;cfi` | Toggles automatic deleting of invites posted in the channel. Does not negate the ;srvrfilterinv enabled setting. Does not affect Bot Owner. | `;cfi`
`;srvrfilterwords` `;sfw` | Toggles automatic deleting of messages containing forbidden words on the server. Does not affect Bot Owner. | `;sfw`
`;chnlfilterwords` `;cfw` | Toggles automatic deleting of messages containing banned words on the channel. Does not negate the ;srvrfilterwords enabled setting. Does not affect bot owner. | `;cfw`
`;fw` | Adds or removes (if it exists) a word from the list of filtered words. Use`;sfw` or `;cfw` to toggle filtering. | `;fw poop`
`;lstfilterwords` `;lfw` | Shows a list of filtered words. | `;lfw`
###### [Back to TOC](#table-of-contents)
### 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`
`~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`
`~randomdog` `~woof` | Shows a random dog image. | `~woof`
`~img` `~i` | Pulls the first image found using a search parameter. Use ~ir for different results. | `~i cute kitten`
`~ir` | Pulls a random image using a search parameter. | `~ir cute kitten`
`~lmgtfy` | Google something for an idiot. | `~lmgtfy query`
`~shorten` | Attempts to shorten an URL, if it fails, returns the input URL. | `~shorten https://google.com`
`~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`
`~urbandict` `~ud` | Searches Urban Dictionary for a word. | `~ud Pineapple`
`~#` | 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"`
`~revimg` | Returns a google reverse image search for an image from a link. | `~revimg Image link`
`~safebooru` | Shows a random image from safebooru with a given tag. Tag is optional but preferred. (multiple tags are appended with +) | `~safebooru yuri+kissing`
`~wikipedia` `~wiki` | Gives you back a wikipedia link | `~wiki query`
`~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`
`~lolban` | Shows top banned champions ordered by ban rate. | `~lolban`
`~memelist` | Pulls a list of memes you can use with `~memegen` from http://memegen.link/templates/ | `~memelist`
`~memegen` | Generates a meme from memelist with top and bottom text. | `~memegen biw "gets iced coffee" "in the winter"`
`~anime` `~ani` `~aq` | Queries anilist for an anime and shows the first result. | `~ani aquarion evol`
`~manga` `~mang` `~mq` | Queries anilist for a manga and shows the first result. | `~mq Shingeki no kyojin`
`~yomama` `~ym` | Shows a random joke from <http://api.yomomma.info/> | `~ym`
`~randjoke` `~rj` | Shows a random joke from <http://tambal.azurewebsites.net/joke/random> | `~rj`
`~chucknorris` `~cn` | Shows a random chucknorris joke from <http://tambal.azurewebsites.net/joke/random> | `~cn`
`~wowjoke` | Get one of Kwoth's penultimate WoW jokes. | `~wowjoke`
`~magicitem` `~mi` | Shows a random magicitem from <https://1d4chan.org/wiki/List_of_/tg/%27s_magic_items> | `~mi`
`~osu` | Shows osu stats for a player. | `~osu Name` or `~osu Name taiko`
`~osub` | Shows information about an osu beatmap. | `~osub https://osu.ppy.sh/s/127712`
`~osu5` | Displays a user's top 5 plays. | `~osu5 Name`
`~placelist` | Shows the list of available tags for the `~place` command. | `~placelist`
`~place` | Shows a placeholder image of a given tag. Use `~placelist` to see all available tags. You can specify the width and height of the image as the last two optional arguments. | `~place Cage` or `~place steven 500 400`
`~pokemon` `~poke` | Searches for a pokemon. | `~poke Sylveon`
`~pokemonability` `~pokeab` | Searches for a pokemon ability. | `~pokeab overgrow`
`~xkcd` | Shows a XKCD comic. No arguments will retrieve random one. Number argument will retrieve a specific comic, and "latest" will get the latest one. | `~xkcd` or `~xkcd 1400` or `~xkcd latest`
`~translate` `~trans` | Translates from>to text. From the given language to the destination language. | `~trans en>fr Hello`
`~autotrans` `~at` | Starts automatic translation of all messages by users who set their `~atl` in this channel. You can set "del" argument to automatically delete all translated user messages. **Requires Administrator server permission.** **Bot Owner only.** | `~at` or `~at del`
`~autotranslang` `~atl` | `~atl en>fr` | Sets your source and target language to be used with `~at`. Specify no arguments to remove previously set value.
`~translangs` | Lists the valid languages for translation. | `~translangs`
`~hitbox` `~hb` | Notifies this channel when a certain user starts streaming. **Requires ManageMessages server permission.** | `~hitbox SomeStreamer`
`~twitch` `~tw` | Notifies this channel when a certain user starts streaming. **Requires ManageMessages server permission.** | `~twitch SomeStreamer`
`~beam` `~bm` | Notifies this channel when a certain user starts streaming. **Requires ManageMessages server permission.** | `~beam SomeStreamer`
`~liststreams` `~ls` | Lists all streams you are following on this server. | `~ls`
`~removestream` `~rms` | Removes notifications of a certain streamer 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`
`~autotranslang` `~atl` | `~atl en>fr` | Sets your source and target language to be used with `~at`. Specify no arguments to remove previously set value.
`~translangs` | Lists the valid languages for translation. | `~translangs`
`~xkcd` | Shows a XKCD comic. No arguments will retrieve random one. Number argument will retrieve a specific comic, and "latest" will get the latest one. | `~xkcd` or `~xkcd 1400` or `~xkcd latest`
`~pokemon` `~poke` | Searches for a pokemon. | `~poke Sylveon`
`~pokemonability` `~pokeab` | Searches for a pokemon ability. | `~pokeab overgrow`
`~placelist` | Shows the list of available tags for the `~place` command. | `~placelist`
`~place` | Shows a placeholder image of a given tag. Use `~placelist` to see all available tags. You can specify the width and height of the image as the last two optional arguments. | `~place Cage` or `~place steven 500 400`
`~overwatch` `~ow` | Show's basic stats on a player (competitive rank, playtime, level etc) Region codes are: `eu` `us` `cn` `kr` | `~ow us Battletag#1337` or `~overwatch eu Battletag#2016`
`~osu` | Shows osu stats for a player. | `~osu Name` or `~osu Name taiko`
`~osub` | Shows information about an osu beatmap. | `~osub https://osu.ppy.sh/s/127712`
`~osu5` | Displays a user's top 5 plays. | `~osu5 Name`
`~yomama` `~ym` | Shows a random joke from <http://api.yomomma.info/> | `~ym`
`~randjoke` `~rj` | Shows a random joke from <http://tambal.azurewebsites.net/joke/random> | `~rj`
`~chucknorris` `~cn` | Shows a random chucknorris joke from <http://tambal.azurewebsites.net/joke/random> | `~cn`
`~wowjoke` | Get one of Kwoth's penultimate WoW jokes. | `~wowjoke`
`~magicitem` `~mi` | Shows a random magicitem from <https://1d4chan.org/wiki/List_of_/tg/%27s_magic_items> | `~mi`
`~anime` `~ani` `~aq` | Queries anilist for an anime and shows the first result. | `~ani aquarion evol`
`~manga` `~mang` `~mq` | Queries anilist for a manga and shows the first result. | `~mq Shingeki no kyojin`
`~weather` `~we` | Shows weather data for a specified city. You can also specify a country after a comma. | `~we Moscow, RU`
`~youtube` `~yt` | Searches youtubes and shows the first result | `~yt query`
`~imdb` `~omdb` | Queries omdb for movies or series, show first result. | `~imdb Batman vs Superman`
`~randomcat` `~meow` | Shows a random cat image. | `~meow`
`~randomdog` `~woof` | Shows a random dog image. | `~woof`
`~image` `~img` | Pulls the first image found using a search parameter. Use ~rimg for different results. | `~img cute kitten`
`~randomimage` `~rimg` | Pulls a random image using a search parameter. | `~rimg cute kitten`
`~lmgtfy` | Google something for an idiot. | `~lmgtfy query`
`~shorten` | Attempts to shorten an URL, if it fails, returns the input URL. | `~shorten https://google.com`
`~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"`
`~revimg` | Returns a google reverse image search for an image from a link. | `~revimg Image link`
`~safebooru` | Shows a random image from safebooru with a given tag. Tag is optional but preferred. (multiple tags are appended with +) | `~safebooru yuri+kissing`
`~wikipedia` `~wiki` | Gives you back a wikipedia link | `~wiki query`
`~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"`
`~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`
`~lolban` | Shows top banned champions ordered by ban rate. | `~lolban`
`~memelist` | Pulls a list of memes you can use with `~memegen` from http://memegen.link/templates/ | `~memelist`
`~memegen` | Generates a meme from memelist with top and bottom text. | `~memegen biw "gets iced coffee" "in the winter"`
###### [Back to TOC](#table-of-contents)
### Utility
Command and aliases | Description | Usage
----------------|--------------|-------
`.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`
`.userid` `.uid` | Shows user ID. | `.uid` or `.uid "@SomeGuy"`
`.channelid` `.cid` | Shows current channel ID. | `.cid`
`.serverid` `.sid` | Shows current server ID. | `.sid`
`.roles` | List roles on this server or a roles of a specific user if specified. Paginated. 20 roles per page. | `.roles 2` or `.roles @Someone`
`.channeltopic` `.ct` | Sends current channel's topic as a message. | `.ct`
`.stats` | Shows some basic stats for Nadeko. | `.stats`
`.showemojis` `.se` | Shows a name and a link to every SPECIAL emoji in the message. | `.se A message full of SPECIAL emojis`
`.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`
`.convertlist` | List of the convertible dimensions and currencies. | `.convertlist`
`.convert` | Convert quantities. Use `.convertlist` to see supported dimensions and currencies. | `.convert m km 1000`
`.remind` | Sends a message to you or a channel after certain amount of time. First argument is me/here/'channelname'. Second argument is time in a descending order (mo>w>d>h>m) example: 1w5d3h10m. Third argument is a (multiword)message. | `.remind me 1d5h Do something` or `.remind #general 1m Start now!`
`.remindtemplate` | Sets message for when the remind is triggered. Available placeholders are %user% - user who ran the command, %message% - Message specified in the remind, %target% - target channel of the remind. **Bot Owner only.** | `.remindtemplate %user%, do %message%!`
`.listquotes` `.liqu` | `.liqu` or `.liqu 3` | Lists all quotes on the server ordered alphabetically. 15 Per page.
`...` | Shows a random quote with a specified name. | `... abc`
`..` | Adds a new quote with the specified name and message. | `.. sayhi Hi`
`.deletequote` `.delq` | Deletes a random quote with the specified keyword. You have to either be server Administrator or the creator of the quote to delete it. | `.delq abc`
`.delallq` `.daq` | Deletes all quotes on a specified keyword. **Requires Administrator server permission.** | `.delallq kek`
`.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`
`.listquotes` `.liqu` | `.liqu` or `.liqu 3` | Lists all quotes on the server ordered alphabetically. 15 Per page.
`...` | Shows a random quote with a specified name. | `... abc`
`..` | Adds a new quote with the specified name and message. | `.. sayhi Hi`
`.deletequote` `.delq` | Deletes a random quote with the specified keyword. You have to either be server Administrator or the creator of the quote to delete it. | `.delq abc`
`.delallq` `.daq` | Deletes all quotes on a specified keyword. **Requires Administrator server permission.** | `.delallq kek`
`.remind` | Sends a message to you or a channel after certain amount of time. First argument is me/here/'channelname'. Second argument is time in a descending order (mo>w>d>h>m) example: 1w5d3h10m. Third argument is a (multiword)message. | `.remind me 1d5h Do something` or `.remind #general 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`
`.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`
`.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`
`.userid` `.uid` | Shows user ID. | `.uid` or `.uid "@SomeGuy"`
`.channelid` `.cid` | Shows current channel ID. | `.cid`
`.serverid` `.sid` | Shows current server ID. | `.sid`
`.roles` | List roles on this server or a roles of a specific user if specified. Paginated. 20 roles per page. | `.roles 2` or `.roles @Someone`
`.channeltopic` `.ct` | Sends current channel's topic as a message. | `.ct`
`.stats` | Shows some basic stats for Nadeko. | `.stats`
`.showemojis` `.se` | Shows a name and a link to every SPECIAL emoji in the message. | `.se A message full of SPECIAL emojis`
`.listservers` | Lists servers the bot is on with some basic info. 15 per page. **Bot Owner only.** | `.listservers 3`
`.activity` | Checks for spammers. **Bot Owner only.** | `.activity`

View File

@ -1,7 +1,7 @@
### How to contribute
1. Make Pull Requests to the **1.0 BRANCH**
2. Keep 1 Pull Request to a single feature
3. Explain what you did in the PR message
1. Make Pull Requests to the [**dev BRANCH**](https://github.com/Kwoth/NadekoBot/tree/dev).
2. Keep 1 Pull Request to a single feature.
3. Explain what you did in the PR message.
Thanks for all the help ^_^
Thanks for all your help ^_^

View File

@ -27,14 +27,23 @@ 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.
| Placeholder | Description | Example Usage | Usage |
|:-----------:|-------------|---------------|-------|
|`%mention`|The `%mention%` placeholder is triggered when you type `@BotName` - It's important to note that if you've given the bot a custom nickname, this trigger won't work!|```.acr "Hello %mention%" I, %mention%, also say hello!```|Input: "Hello @BotName" Output: "I, @BotName, also say hello!"|
|`%mention%`|The `%mention%` placeholder is triggered when you type `@BotName` - It's important to note that if you've given the bot a custom nickname, this trigger won't work!|```.acr "Hello %mention%" I, %mention%, also say hello!```|Input: "Hello @BotName" Output: "I, @BotName, also say hello!"|
|`%user%`|The `%user%` placeholder mentions the person who said the command|`.acr "Who am I?" You are %user%!`|Input: "Who am I?" Output: "You are @Username!"|
|`%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

@ -3,11 +3,11 @@
###Question 1: How do I get Nadeko to join my server?
----
**Answer:** Simply send Nadeko a Direct Message with -h and follow the link. **Only People with the Manage Server permission can add the bot to the server**
**Answer:** Simply send Nadeko a Direct Message with `-h` and follow the link. **Only People with the Manage Server permission can add the bot to the server**
###Question 2: I want to change permissions, but it isn't working!
----
**Answer:** You must have the ;permrole (by default this is the "Nadeko" role, for more details on permissions check [here](http://nadekobot.readthedocs.io/en/1.0/Permissions%20System/ "Permissions"). If you have a role called `Nadeko` but can't assign it, just create a new Role and assign that instead.)
**Answer:** You must have the `;permrole` (by default this is the `Nadeko` role, for more details on permissions check [here](http://nadekobot.readthedocs.io/en/latest/Permissions%20System/ "Permissions"). If you have a role called `Nadeko` but can't assign it it's probably the Bot Role so, just create a **New Role** called `Nadeko` and assign that to yourself instead.)
###Question 3: I want to disable NSFW on my server.
----
@ -27,51 +27,43 @@ If your problem or suggestion is not there, feel free to request/notify us about
--------
**Answer:** You can see the description and usage of certain commands by using `-h command` **i.e** `-h ;sm`.
The whole list of commands can be found [here](http://nadekobot.readthedocs.io/en/1.0/Commands%20List/ "Command List")
The whole list of commands can be found [here](http://nadekobot.readthedocs.io/en/latest/Commands%20List/ "Command List")
###Question 7: Music isn't working?
----
**Answer:** Music is disabled on public Nadeko due to large hosting costs, it will be re-enabled later in the future for donators.
**If you would like music in the meantime, you must host Nadeko yourself**. Be sure you have FFMPEG installed correctly, and have followed the [guide](http://nadekobot.readthedocs.io/en/1.0/guides/Windows%20Guide/) carefully.
**If you would like music in the meantime, you must host Nadeko yourself**. Be sure you have FFMPEG installed correctly, and have followed the [guide](http://nadekobot.readthedocs.io/en/latest/guides/Windows%20Guide/#setting-up-nadekobot-for-music) carefully.
###Question 8: My music is still not working/very laggy?
----
**Answer:** Try changing your discord [location][1], if this doesn't work be sure you have enabled the correct permissions for Nadeko and rebooted since installing FFMPEG.
[1]: https://support.discordapp.com/hc/en-us/articles/216661717-How-do-I-change-my-Voice-Server-Region-
###Question 9: I want to change data in the database (like NadekoFlowers or the pokemontypes of users, but how?
###Question 9: I want to change data in the database like NadekoFlowers or something else but how?
----
**Answer:** Open `/data/NadekoBot.db` using sqlitebrowser (or some alternative), Browse Data, select relevant table, change data, Write changes
**Answer:** Follow the [DB Guide](http://nadekobot.readthedocs.io/en/latest/JSON%20Explanations/#db-files), Open `/data/NadekoBot.db` using **sqlitebrowser** (or some alternative), Browse Data, select relevant table, change data, Write changes and done.
###Question 10: The .greet and .bye commands doesn't work, but everything else is (From @Kong)
###Question 10: The .greet and .bye commands doesn't work, but everything else is!
-----
**Answer:** Set a greeting message by using `.greetmsg YourMessageHere` and a bye-message by using `.byemsg YourMessageHere`. Don't forget that `.greet` and `.bye` only apply to users joining a server, not coming online/offline.
###Question 11: How do I import certs on linux?
-------
**Answer:**
`certmgr -ssl https://discordapp.com`
`certmgr -ssl https://gateway.discord.gg`
###Question 12: I made an application, but I can't add that new bot to my server, how do I invite it to my server?
###Question 11: I made an application, but I can't add that new bot to my server, how do I invite it to my server?
----
**Answer:** You need to use oauth link to add it to you server, just copy your CLIENT ID (that's in the same Developer page where you brought your token) and replace `12345678` in the link below: https://discordapp.com/oauth2/authorize?client_id=12345678&scope=bot&permissions=66186303
**Answer:** You need to use oauth link to add it to you server, just copy your **CLIENT ID** (that's in the same [Developer page](https://discordapp.com/developers/applications/me) where you brought your token) and replace `12345678` in the link below: **https://discordapp.com/oauth2/authorize?client_id=`12345678`&scope=bot&permissions=66186303**
Follow this Detailed [Guide](http://discord.kongslien.net/guide.html) if you do not understand.
Follow this Detailed [Guide](http://discord.kongslien.net/guide.html).
###Question 13: I'm building NadekoBot from source, but I get hundreds of (namespace) errors without changing anything!?
###Question 12: I'm building NadekoBot from source, but I get hundreds of (namespace) errors without changing anything!?
-----
**Answer:** Using Visual Studio, you can solve these errors by going to `Tools` -> `NuGet Package Manager` -> `Manage NuGet Packages for Solution`. Go to the Installed tab, select the Packages that were missing (usually `Newtonsoft.json` and `RestSharp`) and install them for all projects
###Question 14: My bot has all permissions but it's still saying, "Failed to add roles. Bot has insufficient permissions.". How do I fix this?
###Question 13: My bot has all permissions but it's still saying, "Failed to add roles. Bot has insufficient permissions". How do I fix this?
----------
**Answer:** Discord has added a few new features and roles now follow hierarchy. This means you need to place your bot's role above every-other role your server has. [Here's](https://support.discordapp.com/hc/en-us/articles/214836687-Role-Management-101) a link to Discords role management 101.
###Question 15: I've broken permissions and am stuck, can I reset permissions?
----------
**Answer:** Yes, there is a way, in one easy command! Just run `.resetperms`
**Answer:** Discord has added few new features and the roles now follows the role hierarchy which means you need to place your bot's role above every-other role your server has to fix the role hierarchy issue. [Here's](https://support.discordapp.com/hc/en-us/articles/214836687-Role-Management-101) a link to Discords role management 101.
**Please Note:** *The bot can only set/add all roles below its own highest role. It can not assign it's "highest role" to anyone else.*
###Question 14: I've broken permissions and am stuck, can I reset permissions?
----------
**Answer:** Yes, there is a way, in one easy command! Just run `.resetperms` and all the permissions you've set through **Permissions Module** will reset.

View File

@ -59,14 +59,23 @@ Additional options
Config.json
===========
`config.json` is now removed with the addition of `NadekoBot.db` so if you have Nadeko 0.9x follow the [upgrading guide](http://nadekobot.readthedocs.io/en/latest/guides/Upgrading%20Guide/)
`config.json` is now removed with the addition of `NadekoBot.db` so if you have Nadeko 0.9x follow the [upgrading guide](http://nadekobot.readthedocs.io/en/latest/guides/Upgrading%20Guide/) to upgrade your bot.
DB files
========
Nadeko uses few db files in order to open the database
- `NadekoBot\src\NadekoBot\bin\Release\netcoreapp1.0\data\NadekoBot.db` (1.0)
- `data/NadekoBot.sqlite` (0.9x)
you will need [DB Browser for SQLite](http://sqlitebrowser.org/) to make changes to any settings you want and click on "write changes" at the top.
Nadeko uses few db files in order to open these database files `NadekoBot\src\NadekoBot\bin\Release\netcoreapp1.0\data\NadekoBot.db` (1.0) or `data\NadekoBot.sqlite` (0.9x) you will need [DB Browser for SQLite](http://sqlitebrowser.org/).
To make changes
- go to **Browse Data** tab
- click on **Table** drop-down list
- choose the table you want to edit
- click on the cell you want to edit
- edit it on the right-hand side
- click on **Apply**
- click on **Write Changes**
and that will save all the changes.
![nadekodb](https://cdn.discordapp.com/attachments/251504306010849280/254067055240806400/nadekodb.gif)

View File

@ -43,7 +43,7 @@ Commonly Asked Questions
---------------
###How do I create a music DJ?
To allow users to only see the current song and have a DJ role for queuing follow these five steps:
To allow users to only see the current song and have a DJ role for queuing follow these steps:
1. `;sm Music disable`
@ -53,17 +53,13 @@ To allow users to only see the current song and have a DJ role for queuing follo
* Enables the "nowplaying" command for everyone
3. `;sc !!getlink enable`
* Enables the "getlink" command for everyone
4. `;sc !!listqueue enable`
3. `;sc !!listqueue enable`
* Enables the "listqueue" command for everyone
5. `;rm Music enable DJ`
4. `;rm Music enable DJ`
* Enables all the music commands only for the DJ role
* Enables all music commands only for the DJ role
###How do I create a NSFW channel?

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,9 +1,11 @@
###Upgrading Nadeko from an older release
##Upgrading Nadeko from an older release
- Follow the Windows Guide/Linux Guide/OS X Guide linked on the left.
- Navigate to your old `Nadeko` folder and copy `credentials.json` and the `/data/` folder.
- Paste this into the new Nadeko's `/NadekoBot/src/NadekoBot/` folder.
**If you have NadekoBot 0.9x**
- Follow the [Windows Guide](http://nadekobot.readthedocs.io/en/latest/guides/Windows%20Guide/)/[Linux Guide](http://nadekobot.readthedocs.io/en/latest/guides/Linux%20Guide/)/[OS X Guide](http://nadekobot.readthedocs.io/en/latest/guides/OSX%20Guide/) and install **NadekoBot 1.0**.
- Navigate to your **old** `Nadeko` folder and copy `credentials.json` file and the `data` folder.
- Paste them into **NadekoBot 1.0** `/NadekoBot/src/NadekoBot/` folder.
- If it asks you to overwrite files, it is fine to do so.
- Now launch new Nadeko as the guide describes.
- In any channel, run the `.migratedata` command - nadeko will now migrate your old data.
- Restart nadeko and everything should work as expected!
- Next launch your **new** Nadeko as the guide describes, if it is not already running.
- In any channel, run the `.migratedata` [command](http://nadekobot.readthedocs.io/en/latest/Commands%20List/) and Nadeko will start migrating your old data.
- Once that is done **restart** Nadeko and everything should work as expected!

View File

@ -1,3 +1,3 @@
{
"projects": [ "discord.net/src", "src" ]
"projects": [ "Discord.Net/src", "src" ]
}

View File

@ -4,9 +4,11 @@ TITLE Downloading NadekoBot, please wait
SET root=%~dp0
CD /D %root%
SET rootdir=%cd%
SET build1=%root%NadekoInstall_Temp\NadekoBot\discord.net\src\Discord.Net\
SET build2=%root%NadekoInstall_Temp\NadekoBot\discord.net\src\Discord.Net.Commands\
SET build3=%root%NadekoInstall_Temp\NadekoBot\src\NadekoBot\
SET build1=%root%NadekoInstall_Temp\NadekoBot\Discord.Net\src\Discord.Net.Core\
SET build2=%root%NadekoInstall_Temp\NadekoBot\Discord.Net\src\Discord.Net.Rest\
SET build3=%root%NadekoInstall_Temp\NadekoBot\Discord.Net\src\Discord.Net.WebSocket\
SET build4=%root%NadekoInstall_Temp\NadekoBot\Discord.Net\src\Discord.Net.Commands\
SET build5=%root%NadekoInstall_Temp\NadekoBot\src\NadekoBot\
SET installtemp=%root%NadekoInstall_Temp\
::Deleting traces of last setup for the sake of clean folders, if by some miracle it still exists
IF EXIST %installtemp% ( RMDIR %installtemp% /S /Q >nul 2>&1)
@ -32,6 +34,10 @@ CD /D %build2%
dotnet restore >nul 2>&1
CD /D %build3%
dotnet restore >nul 2>&1
CD /D %build4%
dotnet restore >nul 2>&1
CD /D %build5%
dotnet restore >nul 2>&1
dotnet build --configuration Release >nul 2>&1
::Attempts to backup old files if they currently exist in the same folder as the batch file
IF EXIST "%root%NadekoBot\" (GOTO :backupinstall)

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

@ -4,9 +4,11 @@ TITLE Downloading NadekoBot, please wait
SET root=%~dp0
CD /D %root%
SET rootdir=%cd%
SET build1=%root%NadekoInstall_Temp\NadekoBot\discord.net\src\Discord.Net\
SET build2=%root%NadekoInstall_Temp\NadekoBot\discord.net\src\Discord.Net.Commands\
SET build3=%root%NadekoInstall_Temp\NadekoBot\src\NadekoBot\
SET build1=%root%NadekoInstall_Temp\NadekoBot\Discord.Net\src\Discord.Net.Core\
SET build2=%root%NadekoInstall_Temp\NadekoBot\Discord.Net\src\Discord.Net.Rest\
SET build3=%root%NadekoInstall_Temp\NadekoBot\Discord.Net\src\Discord.Net.WebSocket\
SET build4=%root%NadekoInstall_Temp\NadekoBot\Discord.Net\src\Discord.Net.Commands\
SET build5=%root%NadekoInstall_Temp\NadekoBot\src\NadekoBot\
SET installtemp=%root%NadekoInstall_Temp\
::Deleting traces of last setup for the sake of clean folders, if by some miracle it still exists
IF EXIST %installtemp% ( RMDIR %installtemp% /S /Q >nul 2>&1)
@ -32,6 +34,10 @@ CD /D %build2%
dotnet restore >nul 2>&1
CD /D %build3%
dotnet restore >nul 2>&1
CD /D %build4%
dotnet restore >nul 2>&1
CD /D %build5%
dotnet restore >nul 2>&1
dotnet build --configuration Release >nul 2>&1
::Attempts to backup old files if they currently exist in the same folder as the batch file
IF EXIST "%root%NadekoBot\" (GOTO :backupinstall)

View File

@ -7,7 +7,7 @@ using System.Linq;
namespace NadekoBot.Attributes
{
[System.AttributeUsage(AttributeTargets.Class)]
sealed class NadekoModuleAttribute : ModuleAttribute
sealed class NadekoModuleAttribute : GroupAttribute
{
//modulename / prefix
private static Dictionary<string, string> modulePrefixes = null;
@ -26,9 +26,9 @@ namespace NadekoBot.Attributes
}
}
public NadekoModuleAttribute(string moduleName, string defaultPrefix) : base(GetModulePrefix(moduleName, defaultPrefix))
public NadekoModuleAttribute(string moduleName, string defaultPrefix) : base(GetModulePrefix(moduleName, defaultPrefix), moduleName)
{
AppendSpace = false;
//AppendSpace = false;
}
private static string GetModulePrefix(string moduleName, string defaultPrefix)

View File

@ -1,12 +1,11 @@
using System.Threading.Tasks;
using Discord.Commands;
using Discord;
namespace NadekoBot.Attributes
{
public class OwnerOnlyAttribute : PreconditionAttribute
{
public override Task<PreconditionResult> CheckPermissions(IUserMessage context, Command executingCommand, object moduleInstance) =>
Task.FromResult((NadekoBot.Credentials.IsOwner(context.Author) ? PreconditionResult.FromSuccess() : PreconditionResult.FromError("Not owner")));
public override Task<PreconditionResult> CheckPermissions(ICommandContext context, CommandInfo executingCommand,IDependencyMap depMap) =>
Task.FromResult((NadekoBot.Credentials.IsOwner(context.User) ? PreconditionResult.FromSuccess() : PreconditionResult.FromError("Not owner")));
}
}

View File

@ -0,0 +1,24 @@
using Discord.Commands;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static NadekoBot.Modules.Permissions.Permissions;
namespace NadekoBot.DataStructures
{
public struct ExecuteCommandResult
{
public readonly CommandInfo CommandInfo;
public readonly PermissionCache PermissionCache;
public readonly IResult Result;
public ExecuteCommandResult(CommandInfo commandInfo, PermissionCache cache, IResult result)
{
this.CommandInfo = commandInfo;
this.PermissionCache = cache;
this.Result = result;
}
}
}

View File

@ -1,6 +1,4 @@
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
namespace NadekoBot.Migrations
{

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,36 @@
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,41 @@
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

@ -2,10 +2,7 @@
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
{
@ -423,34 +420,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 +519,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 +701,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 +811,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")

File diff suppressed because it is too large Load Diff

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;
@ -66,7 +67,7 @@ namespace NadekoBot.Modules.Administration
}
[Group]
public class AntiRaidCommands
public class AntiRaidCommands : ModuleBase
{
private static ConcurrentDictionary<ulong, AntiRaidSetting> antiRaidGuilds =
new ConcurrentDictionary<ulong, AntiRaidSetting>();
@ -74,58 +75,52 @@ 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) =>
{
var msg = imsg as IUserMessage;
if (msg == null || msg.Author.IsBot)
return Task.CompletedTask;
var channel = msg.Channel as ITextChannel;
if (channel == null)
return Task.CompletedTask;
var t = Task.Run(async () =>
try
{
try
var msg = imsg as IUserMessage;
if (msg == null || msg.Author.IsBot)
return;
var channel = msg.Channel as ITextChannel;
if (channel == null)
return;
AntiSpamSetting spamSettings;
if (!antiSpamGuilds.TryGetValue(channel.Guild.Id, out spamSettings))
return;
var stats = spamSettings.UserStats.AddOrUpdate(msg.Author.Id, new UserSpamStats(msg.Content),
(id, old) => { old.ApplyNextMessage(msg.Content); return old; });
if (stats.Count >= spamSettings.MessageThreshold)
{
AntiSpamSetting spamSettings;
if (!antiSpamGuilds.TryGetValue(channel.Guild.Id, out spamSettings))
return;
var stats = spamSettings.UserStats.AddOrUpdate(msg.Author.Id, new UserSpamStats(msg.Content),
(id, old) => { old.ApplyNextMessage(msg.Content); return old; });
if (stats.Count >= spamSettings.MessageThreshold)
if (spamSettings.UserStats.TryRemove(msg.Author.Id, out stats))
{
if (spamSettings.UserStats.TryRemove(msg.Author.Id, out stats))
{
await PunishUsers(spamSettings.Action, await GetMuteRole(channel.Guild), ProtectionType.Spamming, (IGuildUser)msg.Author)
.ConfigureAwait(false);
}
await PunishUsers(spamSettings.Action, ProtectionType.Spamming, (IGuildUser)msg.Author)
.ConfigureAwait(false);
}
}
catch { }
});
return Task.CompletedTask;
}
catch { }
};
client.UserJoined += (usr) =>
NadekoBot.Client.UserJoined += async (usr) =>
{
if (usr.IsBot)
return Task.CompletedTask;
AntiRaidSetting settings;
if (!antiRaidGuilds.TryGetValue(usr.Guild.Id, out settings))
return Task.CompletedTask;
var t = Task.Run(async () =>
try
{
if (usr.IsBot)
return;
AntiRaidSetting settings;
if (!antiRaidGuilds.TryGetValue(usr.Guild.Id, out settings))
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.MuteUser(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;
@ -195,30 +190,28 @@ namespace NadekoBot.Modules.Administration
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[RequirePermission(GuildPermission.Administrator)]
public async Task AntiRaid(IUserMessage imsg, int userThreshold, int seconds, PunishmentAction action)
[RequireUserPermission(GuildPermission.Administrator)]
public async Task AntiRaid(int userThreshold, int seconds, PunishmentAction action)
{
var channel = (ITextChannel)imsg.Channel;
if (userThreshold < 2 || userThreshold > 30)
{
await channel.SendMessageAsync("❗User threshold must be between **2** and **30**.").ConfigureAwait(false);
await Context.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 Context.Channel.SendErrorAsync("❗Time must be between **2** and **300** seconds.").ConfigureAwait(false);
return;
}
try
{
await GetMuteRole(channel.Guild).ConfigureAwait(false);
await MuteCommands.GetMuteRole(Context.Guild).ConfigureAwait(false);
}
catch (Exception ex)
{
await channel.SendMessageAsync("⚠️ Failed creating a mute role. Give me ManageRoles permission" +
await Context.Channel.SendConfirmAsync("⚠️ Failed creating a mute role. Give me ManageRoles permission" +
"or create 'nadeko-mute' role with disabled SendMessages and try again.")
.ConfigureAwait(false);
_log.Warn(ex);
@ -231,51 +224,49 @@ namespace NadekoBot.Modules.Administration
Seconds = seconds,
UserThreshold = userThreshold,
};
antiRaidGuilds.AddOrUpdate(channel.Guild.Id, setting, (id, old) => setting);
antiRaidGuilds.AddOrUpdate(Context.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 Context.Channel.SendConfirmAsync($" {Context.User.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)
[RequireUserPermission(GuildPermission.Administrator)]
public async Task AntiSpam(int messageCount=3, PunishmentAction action = PunishmentAction.Mute)
{
var channel = (ITextChannel)imsg.Channel;
if (messageCount < 2 || messageCount > 10)
return;
AntiSpamSetting throwaway;
if (antiSpamGuilds.TryRemove(channel.Guild.Id, out throwaway))
if (antiSpamGuilds.TryRemove(Context.Guild.Id, out throwaway))
{
await channel.SendMessageAsync("🆗 **Anti-Spam feature** has been **disabled** on this server.").ConfigureAwait(false);
await Context.Channel.SendConfirmAsync("🆗 **Anti-Spam feature** has been **disabled** on this server.").ConfigureAwait(false);
}
else
{
try
{
await GetMuteRole(channel.Guild).ConfigureAwait(false);
await MuteCommands.GetMuteRole(Context.Guild).ConfigureAwait(false);
}
catch (Exception ex)
{
await channel.SendMessageAsync("⚠️ Failed creating a mute role. Give me ManageRoles permission" +
await Context.Channel.SendErrorAsync("⚠️ Failed creating a mute role. Give me ManageRoles permission" +
"or create 'nadeko-mute' role with disabled SendMessages and try again.")
.ConfigureAwait(false);
_log.Warn(ex);
return;
}
if (antiSpamGuilds.TryAdd(channel.Guild.Id, new AntiSpamSetting()
if (antiSpamGuilds.TryAdd(Context.Guild.Id, new AntiSpamSetting()
{
Action = action,
MessageThreshold = messageCount,
}))
await channel.SendMessageAsync("✅ **Anti-Spam feature** has been **enabled** on this server.").ConfigureAwait(false);
await Context.Channel.SendConfirmAsync("✅ **Anti-Spam feature** has been **enabled** on this server.").ConfigureAwait(false);
}
}
}
}
}
}

View File

@ -1,10 +1,13 @@
using Discord;
using Discord.Commands;
using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Services;
using NadekoBot.Services.Database.Models;
using NLog;
using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
@ -13,67 +16,72 @@ namespace NadekoBot.Modules.Administration
public partial class Administration
{
[Group]
public class AutoAssignRoleCommands
public class AutoAssignRoleCommands : ModuleBase
{
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) =>
_log = LogManager.GetCurrentClassLogger();
var sw = Stopwatch.StartNew();
AutoAssignedRoles = new ConcurrentDictionary<ulong, ulong>(NadekoBot.AllGuildConfigs.Where(x => x.AutoAssignRoleId != 0)
.ToDictionary(k => k.GuildId, v => v.AutoAssignRoleId));
NadekoBot.Client.UserJoined += async (user) =>
{
var t = Task.Run(async () =>
try
{
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)
return;
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);
}
catch (Exception ex) { _log.Warn(ex); }
});
return Task.CompletedTask;
if (role != null)
await user.AddRolesAsync(role).ConfigureAwait(false);
}
catch (Exception ex) { _log.Warn(ex); }
};
sw.Stop();
_log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s");
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[RequirePermission(GuildPermission.ManageRoles)]
public async Task AutoAssignRole(IUserMessage umsg, [Remainder] IRole role = null)
[RequireUserPermission(GuildPermission.ManageRoles)]
public async Task AutoAssignRole([Remainder] IRole role = null)
{
var channel = (ITextChannel)umsg.Channel;
GuildConfig conf;
using (var uow = DbHandler.UnitOfWork())
{
conf = uow.GuildConfigs.For(channel.Guild.Id);
conf = uow.GuildConfigs.For(Context.Guild.Id, set => set);
if (role == null)
{
conf.AutoAssignRoleId = 0;
ulong throwaway;
AutoAssignedRoles.TryRemove(Context.Guild.Id, out throwaway);
}
else
{
conf.AutoAssignRoleId = role.Id;
AutoAssignedRoles.AddOrUpdate(Context.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 Context.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 Context.Channel.SendConfirmAsync("✅ **Auto assign role** on user join is now **enabled**.").ConfigureAwait(false);
}
}
}

View File

@ -14,89 +14,83 @@ namespace NadekoBot.Modules.Administration
public partial class Administration
{
[Group]
public class CrossServerTextChannel
public class CrossServerTextChannel : ModuleBase
{
public CrossServerTextChannel()
static CrossServerTextChannel()
{
_log = LogManager.GetCurrentClassLogger();
NadekoBot.Client.MessageReceived += (imsg) =>
NadekoBot.Client.MessageReceived += async (imsg) =>
{
if (imsg.Author.IsBot)
return Task.CompletedTask;
var msg = imsg as IUserMessage;
if (msg == null)
return Task.CompletedTask;
var channel = imsg.Channel as ITextChannel;
if (channel == null)
return Task.CompletedTask;
Task.Run(async () =>
try
{
if (msg.Author.Id == NadekoBot.Client.GetCurrentUser().Id) return;
if (imsg.Author.IsBot)
return;
var msg = imsg as IUserMessage;
if (msg == null)
return;
var channel = imsg.Channel as ITextChannel;
if (channel == null)
return;
if (msg.Author.Id == NadekoBot.Client.CurrentUser().Id) return;
foreach (var subscriber in Subscribers)
{
var set = subscriber.Value;
if (!set.Contains(msg.Channel))
if (!set.Contains(channel))
continue;
foreach (var chan in set.Except(new[] { channel }))
{
try { await chan.SendMessageAsync(GetText(channel.Guild, channel, (IGuildUser)msg.Author, msg)).ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
}
}
});
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)]
[OwnerOnly]
public async Task Scsc(IUserMessage msg)
public async Task Scsc()
{
var channel = (ITextChannel)msg.Channel;
var token = new NadekoRandom().Next();
var set = new ConcurrentHashSet<ITextChannel>();
if (Subscribers.TryAdd(token, set))
{
set.Add(channel);
await ((IGuildUser)msg.Author).SendMessageAsync("This is your CSC token:" + token.ToString()).ConfigureAwait(false);
set.Add((ITextChannel)Context.Channel);
await ((IGuildUser)Context.User).SendConfirmAsync("This is your CSC token", token.ToString()).ConfigureAwait(false);
}
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[RequirePermission(GuildPermission.ManageGuild)]
public async Task Jcsc(IUserMessage imsg, int token)
[RequireUserPermission(GuildPermission.ManageGuild)]
public async Task Jcsc(int token)
{
var channel = (ITextChannel)imsg.Channel;
ConcurrentHashSet<ITextChannel> set;
if (!Subscribers.TryGetValue(token, out set))
return;
set.Add(channel);
await channel.SendMessageAsync(":ok:").ConfigureAwait(false);
set.Add((ITextChannel)Context.Channel);
await Context.Channel.SendConfirmAsync("Joined cross server channel.").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[RequirePermission(GuildPermission.ManageGuild)]
public async Task Lcsc(IUserMessage imsg)
[RequireUserPermission(GuildPermission.ManageGuild)]
public async Task Lcsc()
{
var channel = (ITextChannel)imsg.Channel;
foreach (var subscriber in Subscribers)
{
subscriber.Value.TryRemove(channel);
subscriber.Value.TryRemove((ITextChannel)Context.Channel);
}
await channel.SendMessageAsync(":ok:").ConfigureAwait(false);
await Context.Channel.SendMessageAsync("Left cross server channel.").ConfigureAwait(false);
}
}
}

View File

@ -1,8 +1,12 @@
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Services;
using NLog;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
@ -11,13 +15,17 @@ namespace NadekoBot.Modules.Administration
public partial class Administration
{
[Group]
public class DMForwardCommands
public class DMForwardCommands : ModuleBase
{
private static bool ForwardDMs { get; set; }
private static bool ForwardDMsToAllOwners { get; set; }
private static readonly Logger _log;
static DMForwardCommands()
{
_log = LogManager.GetCurrentClassLogger();
using (var uow = DbHandler.UnitOfWork())
{
var config = uow.BotConfig.GetOrCreate();
@ -28,10 +36,8 @@ namespace NadekoBot.Modules.Administration
[NadekoCommand, Usage, Description, Aliases]
[OwnerOnly]
public async Task ForwardMessages(IUserMessage imsg)
public async Task ForwardMessages()
{
var channel = imsg.Channel;
using (var uow = DbHandler.UnitOfWork())
{
var config = uow.BotConfig.GetOrCreate();
@ -39,17 +45,15 @@ namespace NadekoBot.Modules.Administration
uow.Complete();
}
if (ForwardDMs)
await channel.SendMessageAsync("✅ **I will forward DMs from now on.**").ConfigureAwait(false);
await Context.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 Context.Channel.SendConfirmAsync("🆗 **I will stop forwarding DMs from now on.**").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[OwnerOnly]
public async Task ForwardToAll(IUserMessage imsg)
public async Task ForwardToAll()
{
var channel = imsg.Channel;
using (var uow = DbHandler.UnitOfWork())
{
var config = uow.BotConfig.GetOrCreate();
@ -57,27 +61,27 @@ namespace NadekoBot.Modules.Administration
uow.Complete();
}
if (ForwardDMsToAllOwners)
await channel.SendMessageAsync(" **I will forward DMs to all owners.**").ConfigureAwait(false);
await Context.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 Context.Channel.SendConfirmAsync(" **I will forward DMs only to the first owner.**").ConfigureAwait(false);
}
public static async Task HandleDMForwarding(IMessage msg, List<IDMChannel> ownerChannels)
public static async Task HandleDMForwarding(SocketMessage msg, List<IDMChannel> ownerChannels)
{
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

@ -1,12 +1,13 @@
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Services;
using NadekoBot.Services.Database.Models;
using NLog;
using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@ -16,9 +17,9 @@ namespace NadekoBot.Modules.Administration
public partial class Administration
{
[Group]
public class RepeatCommands
public class RepeatCommands : ModuleBase
{
public ConcurrentDictionary<ulong, RepeatRunner> repeaters;
public static ConcurrentDictionary<ulong, RepeatRunner> repeaters { get; }
public class RepeatRunner
{
@ -33,7 +34,7 @@ namespace NadekoBot.Modules.Administration
{
_log = LogManager.GetCurrentClassLogger();
this.Repeater = repeater;
this.Channel = channel ?? NadekoBot.Client.GetGuild(repeater.GuildId)?.GetTextChannel(repeater.ChannelId);
this.Channel = channel ?? NadekoBot.Client.GetGuild(repeater.GuildId)?.GetTextChannelAsync(repeater.ChannelId).GetAwaiter().GetResult();
if (Channel == null)
return;
Task.Run(Run);
@ -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,39 +77,41 @@ namespace NadekoBot.Modules.Administration
}
}
public RepeatCommands()
static RepeatCommands()
{
var _log = LogManager.GetCurrentClassLogger();
var sw = Stopwatch.StartNew();
using (var uow = DbHandler.UnitOfWork())
{
repeaters = new ConcurrentDictionary<ulong, RepeatRunner>(uow.Repeaters.GetAll().Select(r => new RepeatRunner(r)).Where(r => r != null).ToDictionary(r => r.Repeater.ChannelId));
}
sw.Stop();
_log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s");
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[RequirePermission(GuildPermission.ManageMessages)]
public async Task RepeatInvoke(IUserMessage imsg)
[RequireUserPermission(GuildPermission.ManageMessages)]
public async Task RepeatInvoke()
{
var channel = (ITextChannel)imsg.Channel;
RepeatRunner rep;
if (!repeaters.TryGetValue(channel.Id, out rep))
if (!repeaters.TryGetValue(Context.Channel.Id, out rep))
{
await channel.SendMessageAsync(" **No repeating message found on this server.**").ConfigureAwait(false);
await Context.Channel.SendErrorAsync(" **No repeating message found on this server.**").ConfigureAwait(false);
return;
}
rep.Reset();
await channel.SendMessageAsync("🔄 " + rep.Repeater.Message).ConfigureAwait(false);
await Context.Channel.SendMessageAsync("🔄 " + rep.Repeater.Message).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[RequirePermission(GuildPermission.ManageMessages)]
public async Task Repeat(IUserMessage imsg)
[RequireUserPermission(GuildPermission.ManageMessages)]
public async Task Repeat()
{
var channel = (ITextChannel)imsg.Channel;
RepeatRunner rep;
if (repeaters.TryRemove(channel.Id, out rep))
if (repeaters.TryRemove(Context.Channel.Id, out rep))
{
using (var uow = DbHandler.UnitOfWork())
{
@ -110,19 +119,17 @@ namespace NadekoBot.Modules.Administration
await uow.CompleteAsync();
}
rep.Stop();
await channel.SendMessageAsync("✅ **Stopped repeating a message.**").ConfigureAwait(false);
await Context.Channel.SendConfirmAsync("✅ **Stopped repeating a message.**").ConfigureAwait(false);
}
else
await channel.SendMessageAsync(" **No message is repeating.**").ConfigureAwait(false);
await Context.Channel.SendConfirmAsync(" **No message is repeating.**").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[RequirePermission(GuildPermission.ManageMessages)]
public async Task Repeat(IUserMessage imsg, int minutes, [Remainder] string message)
[RequireUserPermission(GuildPermission.ManageMessages)]
public async Task Repeat(int minutes, [Remainder] string message)
{
var channel = (ITextChannel)imsg.Channel;
if (minutes < 1 || minutes > 10080)
return;
@ -131,20 +138,20 @@ namespace NadekoBot.Modules.Administration
RepeatRunner rep;
rep = repeaters.AddOrUpdate(channel.Id, (cid) =>
rep = repeaters.AddOrUpdate(Context.Channel.Id, (cid) =>
{
using (var uow = DbHandler.UnitOfWork())
{
var localRep = new Repeater
{
ChannelId = channel.Id,
GuildId = channel.Guild.Id,
ChannelId = Context.Channel.Id,
GuildId = Context.Guild.Id,
Interval = TimeSpan.FromMinutes(minutes),
Message = message,
};
uow.Repeaters.Add(localRep);
uow.Complete();
return new RepeatRunner(localRep, channel);
return new RepeatRunner(localRep, (ITextChannel)Context.Channel);
}
}, (cid, old) =>
{
@ -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 Context.Channel.SendConfirmAsync($"🔁 Repeating **\"{rep.Repeater.Message}\"** every `{rep.Repeater.Interval.Days} day(s), {rep.Repeater.Interval.Hours} hour(s) and {rep.Repeater.Interval.Minutes} minute(s)`.").ConfigureAwait(false);
}
}
}

View File

@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using NadekoBot.Attributes;
using NadekoBot.Services;
@ -21,23 +20,21 @@ namespace NadekoBot.Modules.Administration
public partial class Administration
{
[Group]
public class Migration
public class Migration : ModuleBase
{
private const int CURRENT_VERSION = 1;
private Logger _log { get; }
private static Logger _log { get; }
public Migration()
static Migration()
{
_log = LogManager.GetCurrentClassLogger();
}
[NadekoCommand, Usage, Description, Aliases]
[OwnerOnly]
public async Task MigrateData(IUserMessage umsg)
public async Task MigrateData()
{
var channel = (ITextChannel)umsg.Channel;
var version = 0;
using (var uow = DbHandler.UnitOfWork())
{
@ -54,12 +51,12 @@ namespace NadekoBot.Modules.Administration
break;
}
}
await umsg.Channel.SendMessageAsync("🆙 **Migration done.**").ConfigureAwait(false);
await Context.Channel.SendMessageAsync("🆙 **Migration done.**").ConfigureAwait(false);
}
catch (Exception ex)
{
_log.Error(ex);
await umsg.Channel.SendMessageAsync("⚠️ **Error while migrating, check `logs` for more informations.**").ConfigureAwait(false);
await Context.Channel.SendMessageAsync("⚠️ **Error while migrating, check `logs` for more informations.**").ConfigureAwait(false);
}
}
@ -109,7 +106,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 +192,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,280 @@
using Discord;
using Discord.Commands;
using Microsoft.EntityFrameworkCore;
using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Services;
using NadekoBot.Services.Database.Models;
using NLog;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
namespace NadekoBot.Modules.Administration
{
public partial class Administration
{
[Group]
public class MuteCommands : ModuleBase
{
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()
{
var _log = LogManager.GetCurrentClassLogger();
var sw = Stopwatch.StartNew();
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;
sw.Stop();
_log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s");
}
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 MuteUser(usr).ConfigureAwait(false);
}
catch (Exception ex)
{
_log.Warn(ex);
}
}
public static async Task MuteUser(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 UnmuteUser(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 (await guild.GetTextChannelsAsync()))
{
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)]
[RequireUserPermission(GuildPermission.ManageRoles)]
[Priority(1)]
public async Task SetMuteRole([Remainder] string name)
{
//var channel = (ITextChannel)Context.Channel;
name = name.Trim();
if (string.IsNullOrWhiteSpace(name))
return;
using (var uow = DbHandler.UnitOfWork())
{
var config = uow.GuildConfigs.For(Context.Guild.Id, set => set);
config.MuteRoleName = name;
GuildMuteRoles.AddOrUpdate(Context.Guild.Id, name, (id, old) => name);
await uow.CompleteAsync().ConfigureAwait(false);
}
await Context.Channel.SendConfirmAsync("☑️ **New mute role set.**").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[RequireUserPermission(GuildPermission.ManageRoles)]
[Priority(0)]
public Task SetMuteRole([Remainder] IRole role)
=> SetMuteRole(role.Name);
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[RequireUserPermission(GuildPermission.ManageRoles)]
[RequireUserPermission(GuildPermission.MuteMembers)]
public async Task Mute(IGuildUser user)
{
try
{
await MuteUser(user).ConfigureAwait(false);
await Context.Channel.SendConfirmAsync($"🔇 **{user}** has been **muted** from text and voice chat.").ConfigureAwait(false);
}
catch
{
await Context.Channel.SendErrorAsync("⚠️ I most likely don't have the permission necessary for that.").ConfigureAwait(false);
}
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[RequireUserPermission(GuildPermission.ManageRoles)]
[RequireUserPermission(GuildPermission.MuteMembers)]
public async Task Unmute(IGuildUser user)
{
try
{
await UnmuteUser(user).ConfigureAwait(false);
await Context.Channel.SendConfirmAsync($"🔉 **{user}** has been **unmuted** from text and voice chat.").ConfigureAwait(false);
}
catch
{
await Context.Channel.SendErrorAsync("⚠️ I most likely don't have the permission necessary for that.").ConfigureAwait(false);
}
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[RequireUserPermission(GuildPermission.ManageRoles)]
public async Task ChatMute(IGuildUser user)
{
try
{
await user.AddRolesAsync(await GetMuteRole(Context.Guild).ConfigureAwait(false)).ConfigureAwait(false);
UserMuted(user, MuteType.Chat);
await Context.Channel.SendConfirmAsync($"✏️🚫 **{user}** has been **muted** from chatting.").ConfigureAwait(false);
}
catch
{
await Context.Channel.SendErrorAsync("⚠️ I most likely don't have the permission necessary for that.").ConfigureAwait(false);
}
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[RequireUserPermission(GuildPermission.ManageRoles)]
public async Task ChatUnmute(IGuildUser user)
{
try
{
await user.RemoveRolesAsync(await GetMuteRole(Context.Guild).ConfigureAwait(false)).ConfigureAwait(false);
UserUnmuted(user, MuteType.Chat);
await Context.Channel.SendConfirmAsync($"✏️✅ **{user}** has been **unmuted** from chatting.").ConfigureAwait(false);
}
catch
{
await Context.Channel.SendErrorAsync("⚠️ I most likely don't have the permission necessary for that.").ConfigureAwait(false);
}
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[RequireUserPermission(GuildPermission.MuteMembers)]
public async Task VoiceMute(IGuildUser user)
{
try
{
await user.ModifyAsync(usr => usr.Mute = true).ConfigureAwait(false);
UserMuted(user, MuteType.Voice);
await Context.Channel.SendConfirmAsync($"🎙🚫 **{user}** has been **voice muted**.").ConfigureAwait(false);
}
catch
{
await Context.Channel.SendErrorAsync("⚠️ I most likely don't have the permission necessary for that.").ConfigureAwait(false);
}
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[RequireUserPermission(GuildPermission.MuteMembers)]
public async Task VoiceUnmute(IGuildUser user)
{
try
{
await user.ModifyAsync(usr => usr.Mute = false).ConfigureAwait(false);
UserUnmuted(user, MuteType.Voice);
await Context.Channel.SendConfirmAsync($"🎙✅ **{user}** has been **voice unmuted**.").ConfigureAwait(false);
}
catch
{
await Context.Channel.SendErrorAsync("⚠️ I most likely don't have the permission necessary for that.").ConfigureAwait(false);
}
}
}
}
}

View File

@ -1,6 +1,5 @@
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Services;
@ -8,6 +7,7 @@ using NadekoBot.Services.Database.Models;
using NLog;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
@ -16,26 +16,25 @@ namespace NadekoBot.Modules.Administration
public partial class Administration
{
[Group]
public class PlayingRotateCommands
public class PlayingRotateCommands : ModuleBase
{
private Logger _log { get; }
private static Logger _log { get; }
public static List<PlayingStatus> RotatingStatusMessages { get; }
public static bool RotatingStatuses { get; private set; } = false;
//todo wtf is with this while(true) in constructor
static PlayingRotateCommands()
{
_log = LogManager.GetCurrentClassLogger();
using (var uow = DbHandler.UnitOfWork())
{
var conf = uow.BotConfig.GetOrCreate();
RotatingStatusMessages = conf.RotatingStatusMessages;
RotatingStatuses = conf.RotatingStatuses;
}
}
public PlayingRotateCommands()
{
_log = LogManager.GetCurrentClassLogger();
Task.Run(async () =>
var t = Task.Run(async () =>
{
var index = 0;
do
@ -72,8 +71,8 @@ namespace NadekoBot.Modules.Administration
public static Dictionary<string, Func<string>> PlayingPlaceholders { get; } =
new Dictionary<string, Func<string>> {
{"%servers%", () => NadekoBot.Client.GetGuilds().Count().ToString()},
{"%users%", () => NadekoBot.Client.GetGuilds().Select(s => s.GetUsers().Count).Sum().ToString()},
{"%servers%", () => NadekoBot.Client.GetGuildsCount().ToString()},
{"%users%", () => NadekoBot.Client.GetGuilds().Sum(s => s.Users.Count).ToString()},
{"%playing%", () => {
var cnt = Music.Music.MusicPlayers.Count(kvp => kvp.Value.CurrentSong != null);
if (cnt != 1) return cnt.ToString();
@ -90,12 +89,9 @@ namespace NadekoBot.Modules.Administration
};
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[OwnerOnly]
public async Task RotatePlaying(IUserMessage umsg)
public async Task RotatePlaying()
{
var channel = (ITextChannel)umsg.Channel;
using (var uow = DbHandler.UnitOfWork())
{
var config = uow.BotConfig.GetOrCreate();
@ -104,18 +100,15 @@ namespace NadekoBot.Modules.Administration
await uow.CompleteAsync();
}
if (RotatingStatuses)
await channel.SendMessageAsync("🆗 **Rotating playing status enabled.**");
await Context.Channel.SendConfirmAsync("🆗 **Rotating playing status enabled.**").ConfigureAwait(false);
else
await channel.SendMessageAsync(" **Rotating playing status disabled.**");
await Context.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)
public async Task AddPlaying([Remainder] string status)
{
var channel = (ITextChannel)umsg.Channel;
using (var uow = DbHandler.UnitOfWork())
{
var config = uow.BotConfig.GetOrCreate();
@ -125,33 +118,27 @@ namespace NadekoBot.Modules.Administration
await uow.CompleteAsync();
}
await channel.SendMessageAsync("✅ **Added.**").ConfigureAwait(false);
await Context.Channel.SendConfirmAsync("✅ **Added.**").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[OwnerOnly]
public async Task ListPlaying(IUserMessage umsg)
public async Task ListPlaying()
{
var channel = (ITextChannel)umsg.Channel;
if (!RotatingStatusMessages.Any())
await channel.SendMessageAsync("❎ **No rotating playing statuses set.**");
await Context.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 Context.Channel.SendConfirmAsync($" {Context.User.Mention} `Here is a list of rotating statuses:`\n\n\t" + string.Join("\n\t", RotatingStatusMessages.Select(rs => $"`{i++}.` {rs.Status}")));
}
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[OwnerOnly]
public async Task RemovePlaying(IUserMessage umsg, int index)
public async Task RemovePlaying(int index)
{
var channel = (ITextChannel)umsg.Channel;
index -= 1;
string msg = "";
@ -166,7 +153,7 @@ namespace NadekoBot.Modules.Administration
RotatingStatusMessages.RemoveAt(index);
await uow.CompleteAsync();
}
await channel.SendMessageAsync($"🗑 **Removed the the playing message:** {msg}").ConfigureAwait(false);
await Context.Channel.SendConfirmAsync($"🗑 **Removed the the playing message:** {msg}").ConfigureAwait(false);
}
}
}

View File

@ -13,12 +13,10 @@ namespace NadekoBot.Modules.Administration
public partial class Administration
{
[Group]
public class RatelimitCommand
public class RatelimitCommand : ModuleBase
{
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,73 +60,71 @@ namespace NadekoBot.Modules.Administration
}
}
public RatelimitCommand()
static RatelimitCommand()
{
this._client = NadekoBot.Client;
this._log = LogManager.GetCurrentClassLogger();
_log = LogManager.GetCurrentClassLogger();
_client.MessageReceived += (umsg) =>
{
var t = Task.Run(async () =>
{
var usrMsg = umsg as IUserMessage;
var channel = usrMsg.Channel as ITextChannel;
NadekoBot.Client.MessageReceived += async (umsg) =>
{
try
{
var usrMsg = umsg as IUserMessage;
if (usrMsg == null)
return;
var channel = usrMsg.Channel as ITextChannel;
if (channel == null || usrMsg.IsAuthor())
return;
Ratelimiter limiter;
if (!RatelimitingChannels.TryGetValue(channel.Id, out limiter))
return;
if (channel == null || usrMsg.IsAuthor())
return;
Ratelimiter limiter;
if (!RatelimitingChannels.TryGetValue(channel.Id, out limiter))
return;
if (limiter.CheckUserRatelimit(usrMsg.Author.Id))
try { await usrMsg.DeleteAsync(); } catch (Exception ex) { _log.Warn(ex); }
});
return Task.CompletedTask;
};
if (limiter.CheckUserRatelimit(usrMsg.Author.Id))
await usrMsg.DeleteAsync();
}
catch (Exception ex) { _log.Warn(ex); }
};
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[RequirePermission(GuildPermission.ManageMessages)]
public async Task Slowmode(IUserMessage umsg)
[RequireUserPermission(GuildPermission.ManageMessages)]
public async Task Slowmode()
{
var channel = (ITextChannel)umsg.Channel;
Ratelimiter throwaway;
if (RatelimitingChannels.TryRemove(channel.Id, out throwaway))
if (RatelimitingChannels.TryRemove(Context.Channel.Id, out throwaway))
{
throwaway.cancelSource.Cancel();
await channel.SendMessageAsync(" **Slow mode disabled.**").ConfigureAwait(false);
await Context.Channel.SendConfirmAsync(" Slow mode disabled.").ConfigureAwait(false);
return;
}
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[RequirePermission(GuildPermission.ManageMessages)]
public async Task Slowmode(IUserMessage umsg, int msg, int perSec)
[RequireUserPermission(GuildPermission.ManageMessages)]
public async Task Slowmode(int msg, int perSec)
{
await Slowmode(umsg).ConfigureAwait(false); // disable if exists
var channel = (ITextChannel)umsg.Channel;
await Slowmode().ConfigureAwait(false); // disable if exists
if (msg < 1 || perSec < 1 || msg > 100 || perSec > 3600)
{
await channel.SendMessageAsync("⚠️ `Invalid parameters.`");
await Context.Channel.SendErrorAsync("⚠️ Invalid parameters.");
return;
}
var toAdd = new Ratelimiter()
{
ChannelId = channel.Id,
ChannelId = Context.Channel.Id,
MaxMessages = msg,
PerSeconds = perSec,
};
if(RatelimitingChannels.TryAdd(channel.Id, toAdd))
if(RatelimitingChannels.TryAdd(Context.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 Context.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;
@ -15,95 +16,95 @@ namespace NadekoBot.Modules.Administration
public partial class Administration
{
[Group]
public class SelfAssignedRolesCommands
public class SelfAssignedRolesCommands : ModuleBase
{
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[RequirePermission(GuildPermission.ManageMessages)]
public async Task AdSarm(IUserMessage imsg)
[RequireUserPermission(GuildPermission.ManageMessages)]
public async Task AdSarm()
{
var channel = (ITextChannel)imsg.Channel;
bool newval;
using (var uow = DbHandler.UnitOfWork())
{
var config = uow.GuildConfigs.For(channel.Guild.Id);
var config = uow.GuildConfigs.For(Context.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 Context.Channel.SendConfirmAsync($" Automatic deleting of `iam` and `iamn` confirmations has been {(newval ? "**enabled**" : "**disabled**")}.")
.ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[RequirePermission(GuildPermission.ManageRoles)]
public async Task Asar(IUserMessage umsg, [Remainder] IRole role)
[RequireUserPermission(GuildPermission.ManageRoles)]
public async Task Asar([Remainder] IRole role)
{
var channel = (ITextChannel)umsg.Channel;
IEnumerable<SelfAssignedRole> roles;
string msg;
using (var uow = DbHandler.UnitOfWork())
{
roles = uow.SelfAssignedRoles.GetFromGuild(channel.Guild.Id);
if (roles.Any(s => s.RoleId == role.Id && s.GuildId == role.GuildId))
roles = uow.SelfAssignedRoles.GetFromGuild(Context.Guild.Id);
if (roles.Any(s => s.RoleId == role.Id && s.GuildId == role.Guild.Id))
{
msg = $"💢 Role **{role.Name}** is already in the list.";
await Context.Channel.SendMessageAsync($"💢 Role **{role.Name}** is already in the list.").ConfigureAwait(false);
return;
}
else
{
uow.SelfAssignedRoles.Add(new SelfAssignedRole {
RoleId = role.Id,
GuildId = role.GuildId
GuildId = role.Guild.Id
});
await uow.CompleteAsync();
msg = $"🆗 Role **{role.Name}** added to the list.";
}
}
await channel.SendMessageAsync(msg.ToString()).ConfigureAwait(false);
await Context.Channel.SendConfirmAsync(msg.ToString()).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[RequirePermission(GuildPermission.ManageRoles)]
public async Task Rsar(IUserMessage umsg, [Remainder] IRole role)
[RequireUserPermission(GuildPermission.ManageRoles)]
public async Task Rsar([Remainder] IRole role)
{
var channel = (ITextChannel)umsg.Channel;
//var channel = (ITextChannel)Context.Channel;
bool success;
using (var uow = DbHandler.UnitOfWork())
{
success = uow.SelfAssignedRoles.DeleteByGuildAndRoleId(role.GuildId, role.Id);
success = uow.SelfAssignedRoles.DeleteByGuildAndRoleId(role.Guild.Id, role.Id);
await uow.CompleteAsync();
}
if (!success)
{
await channel.SendMessageAsync("❎ That role is not self-assignable.").ConfigureAwait(false);
await Context.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 Context.Channel.SendConfirmAsync($"🗑 **{role.Name}** has been removed from the list of self-assignable roles.").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Lsar(IUserMessage umsg)
public async Task Lsar()
{
var channel = (ITextChannel)umsg.Channel;
//var channel = (ITextChannel)Context.Channel;
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(Context.Guild.Id).ToList();
roleCnt = roleModels.Count;
msg.AppendLine();
foreach (var roleModel in roleModels)
{
var role = channel.Guild.Roles.FirstOrDefault(r => r.Id == roleModel.RoleId);
var role = Context.Guild.Roles.FirstOrDefault(r => r.Id == roleModel.RoleId);
if (role == null)
{
uow.SelfAssignedRoles.Remove(roleModel);
@ -119,61 +120,61 @@ namespace NadekoBot.Modules.Administration
}
await uow.CompleteAsync();
}
await channel.SendMessageAsync(msg.ToString() + "\n\n" + removeMsg.ToString()).ConfigureAwait(false);
await Context.Channel.SendConfirmAsync($" There are `{roleCnt}` self assignable roles:", msg.ToString() + "\n\n" + removeMsg.ToString()).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[RequirePermission(GuildPermission.ManageRoles)]
public async Task Tesar(IUserMessage umsg)
[RequireUserPermission(GuildPermission.ManageRoles)]
public async Task Tesar()
{
var channel = (ITextChannel)umsg.Channel;
//var channel = (ITextChannel)Context.Channel;
bool areExclusive;
using (var uow = DbHandler.UnitOfWork())
{
var config = uow.GuildConfigs.For(channel.Guild.Id);
var config = uow.GuildConfigs.For(Context.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 Context.Channel.SendConfirmAsync(" Self assigned roles are now " + exl);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Iam(IUserMessage umsg, [Remainder] IRole role)
public async Task Iam([Remainder] IRole role)
{
var channel = (ITextChannel)umsg.Channel;
var guildUser = (IGuildUser)umsg.Author;
var usrMsg = (IUserMessage)umsg;
//var channel = (ITextChannel)Context.Channel;
var guildUser = (IGuildUser)Context.User;
GuildConfig conf;
IEnumerable<SelfAssignedRole> roles;
using (var uow = DbHandler.UnitOfWork())
{
conf = uow.GuildConfigs.For(channel.Guild.Id);
roles = uow.SelfAssignedRoles.GetFromGuild(channel.Guild.Id);
conf = uow.GuildConfigs.For(Context.Guild.Id, set => set);
roles = uow.SelfAssignedRoles.GetFromGuild(Context.Guild.Id);
}
SelfAssignedRole roleModel;
if ((roleModel = roles.FirstOrDefault(r=>r.RoleId == role.Id)) == null)
{
await channel.SendMessageAsync("💢 That role is not self-assignable.").ConfigureAwait(false);
await Context.Channel.SendErrorAsync("That role is not self-assignable.").ConfigureAwait(false);
return;
}
if (guildUser.Roles.Contains(role))
if (guildUser.RoleIds.Contains(role.Id))
{
await channel.SendMessageAsync($"❎ You already have **{role.Name}** role.").ConfigureAwait(false);
await Context.Channel.SendErrorAsync($"You already have **{role.Name}** role.").ConfigureAwait(false);
return;
}
if (conf.ExclusiveSelfAssignedRoles)
{
var sameRoles = guildUser.Roles.Where(r => roles.Any(rm => rm.RoleId == r.Id));
if (sameRoles.Any())
var sameRoleId = guildUser.RoleIds.Where(r => roles.Select(sar => sar.RoleId).Contains(r)).FirstOrDefault();
var sameRole = Context.Guild.GetRole(sameRoleId);
if (sameRoleId != default(ulong))
{
await channel.SendMessageAsync($"❎ You already have **{sameRoles.FirstOrDefault().Name}** `exclusive self-assigned` role.").ConfigureAwait(false);
await Context.Channel.SendErrorAsync($"You already have **{sameRole?.Name}** `exclusive self-assigned` role.").ConfigureAwait(false);
return;
}
}
@ -183,46 +184,41 @@ 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 Context.Channel.SendErrorAsync($"⚠️ I am unable to add that role to you. `I can't add roles to owners or other roles higher than my role in the role hierarchy.`").ConfigureAwait(false);
Console.WriteLine(ex);
return;
}
var msg = await channel.SendMessageAsync($"🆗 You now have **{role.Name}** role.").ConfigureAwait(false);
var msg = await Context.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);
Context.Message.DeleteAfter(3);
}
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Iamnot(IUserMessage umsg, [Remainder] IRole role)
public async Task Iamnot([Remainder] IRole role)
{
var channel = (ITextChannel)umsg.Channel;
var guildUser = (IGuildUser)umsg.Author;
var guildUser = (IGuildUser)Context.User;
GuildConfig conf;
bool autoDeleteSelfAssignedRoleMessages;
IEnumerable<SelfAssignedRole> roles;
using (var uow = DbHandler.UnitOfWork())
{
conf = uow.GuildConfigs.For(channel.Guild.Id);
roles = uow.SelfAssignedRoles.GetFromGuild(channel.Guild.Id);
autoDeleteSelfAssignedRoleMessages = uow.GuildConfigs.For(Context.Guild.Id, set => set).AutoDeleteSelfAssignedRoleMessages;
roles = uow.SelfAssignedRoles.GetFromGuild(Context.Guild.Id);
}
SelfAssignedRole roleModel;
if ((roleModel = roles.FirstOrDefault(r => r.RoleId == role.Id)) == null)
{
await channel.SendMessageAsync("💢 That role is not self-assignable.").ConfigureAwait(false);
await Context.Channel.SendErrorAsync("💢 That role is not self-assignable.").ConfigureAwait(false);
return;
}
if (!guildUser.Roles.Contains(role))
if (!guildUser.RoleIds.Contains(role.Id))
{
await channel.SendMessageAsync($"❎ You don't have **{role.Name}** role.").ConfigureAwait(false);
await Context.Channel.SendErrorAsync($"❎ You don't have **{role.Name}** role.").ConfigureAwait(false);
return;
}
try
@ -231,19 +227,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 Context.Channel.SendErrorAsync($"⚠️ I am unable to add that role to you. `I can't remove roles to owners or other roles higher than my role in the role hierarchy.`").ConfigureAwait(false);
return;
}
var msg = await channel.SendMessageAsync($"🆗 You no longer have **{role.Name}** role.").ConfigureAwait(false);
var msg = await Context.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);
Context.Message.DeleteAfter(3);
}
}
}

View File

@ -1,7 +1,11 @@
using Discord;
using Discord.Commands;
using NadekoBot.Attributes;
using NadekoBot.Extensions;
using System;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
namespace NadekoBot.Modules.Administration
@ -9,42 +13,162 @@ namespace NadekoBot.Modules.Administration
public partial class Administration
{
[Group]
class SelfCommands
class SelfCommands : ModuleBase
{
private ShardedDiscordClient _client;
public SelfCommands(ShardedDiscordClient client)
{
this._client = client;
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[OwnerOnly]
public async Task Leave(IUserMessage umsg, [Remainder] string guildStr)
public async Task Leave([Remainder] string guildStr)
{
var channel = (ITextChannel)umsg.Channel;
guildStr = guildStr.Trim().ToUpperInvariant();
var server = _client.GetGuilds().FirstOrDefault(g => g.Id.ToString().Trim().ToUpperInvariant() == guildStr) ??
_client.GetGuilds().FirstOrDefault(g => g.Name.Trim().ToUpperInvariant() == guildStr);
var server = NadekoBot.Client.GetGuilds().FirstOrDefault(g => g.Id.ToString() == guildStr) ??
NadekoBot.Client.GetGuilds().FirstOrDefault(g => g.Name.Trim().ToUpperInvariant() == guildStr);
if (server == null)
{
await channel.SendMessageAsync("⚠️ Cannot find that server").ConfigureAwait(false);
await Context.Channel.SendErrorAsync("⚠️ Cannot find that server").ConfigureAwait(false);
return;
}
if (server.OwnerId != _client.GetCurrentUser().Id)
if (server.OwnerId != NadekoBot.Client.CurrentUser().Id)
{
await server.LeaveAsync().ConfigureAwait(false);
await channel.SendMessageAsync("✅ Left server " + server.Name).ConfigureAwait(false);
await Context.Channel.SendConfirmAsync("✅ Left server " + server.Name).ConfigureAwait(false);
}
else
{
await server.DeleteAsync().ConfigureAwait(false);
await channel.SendMessageAsync("Deleted server " + server.Name).ConfigureAwait(false);
await Context.Channel.SendConfirmAsync("Deleted server " + server.Name).ConfigureAwait(false);
}
}
[NadekoCommand, Usage, Description, Aliases]
[OwnerOnly]
public async Task Die()
{
try { await Context.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]
[OwnerOnly]
public async Task SetName([Remainder] string newName)
{
if (string.IsNullOrWhiteSpace(newName))
return;
await NadekoBot.Client.CurrentUser().ModifyAsync(u => u.Username = newName).ConfigureAwait(false);
await Context.Channel.SendConfirmAsync($"Bot name changed to **{newName}**").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[OwnerOnly]
public async Task SetStatus([Remainder] SettableUserStatus status)
{
await NadekoBot.Client.SetStatus(status);
await Context.Channel.SendConfirmAsync($"Bot status changed to **{status}**").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[OwnerOnly]
public async Task SetAvatar([Remainder] string img = null)
{
if (string.IsNullOrWhiteSpace(img))
return;
using (var http = new HttpClient())
{
using (var sr = await http.GetStreamAsync(img))
{
var imgStream = new MemoryStream();
await sr.CopyToAsync(imgStream);
imgStream.Position = 0;
await NadekoBot.Client.CurrentUser().ModifyAsync(u => u.Avatar = new Image(imgStream)).ConfigureAwait(false);
}
}
await Context.Channel.SendConfirmAsync("🆒 **New avatar set.**").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[OwnerOnly]
public async Task SetGame([Remainder] string game = null)
{
await NadekoBot.Client.SetGame(game).ConfigureAwait(false);
await Context.Channel.SendConfirmAsync("👾 **New game set.**").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[OwnerOnly]
public async Task SetStream(string url, [Remainder] string name = null)
{
name = name ?? "";
await NadekoBot.Client.SetStream(name, url).ConfigureAwait(false);
await Context.Channel.SendConfirmAsync(" **New stream set.**").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[OwnerOnly]
public async Task Send(string where, [Remainder] string msg = null)
{
if (string.IsNullOrWhiteSpace(msg))
return;
var ids = where.Split('|');
if (ids.Length != 2)
return;
var sid = ulong.Parse(ids[0]);
var server = NadekoBot.Client.GetGuilds().Where(s => s.Id == sid).FirstOrDefault();
if (server == null)
return;
if (ids[1].ToUpperInvariant().StartsWith("C:"))
{
var cid = ulong.Parse(ids[1].Substring(2));
var ch = (await server.GetTextChannelsAsync()).Where(c => c.Id == cid).FirstOrDefault();
if (ch == null)
{
return;
}
await ch.SendMessageAsync(msg).ConfigureAwait(false);
}
else if (ids[1].ToUpperInvariant().StartsWith("U:"))
{
var uid = ulong.Parse(ids[1].Substring(2));
var user = server.Users.Where(u => u.Id == uid).FirstOrDefault();
if (user == null)
{
return;
}
await user.SendMessageAsync(msg).ConfigureAwait(false);
}
else
{
await Context.Channel.SendErrorAsync("⚠️ Invalid format.").ConfigureAwait(false);
}
}
[NadekoCommand, Usage, Description, Aliases]
[OwnerOnly]
public async Task Announce([Remainder] string message)
{
var channels = await Task.WhenAll(NadekoBot.Client.GetGuilds().Select(g =>
g.GetDefaultChannelAsync()
)).ConfigureAwait(false);
if (channels == null)
return;
await Task.WhenAll(channels.Where(c => c != null).Select(c => c.SendConfirmAsync($"🆕 Message from {Context.User} `[Bot Owner]`:", message)))
.ConfigureAwait(false);
await Context.Channel.SendConfirmAsync("🆗").ConfigureAwait(false);
}
}
}
}

View File

@ -1,9 +1,9 @@
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Services;
using NadekoBot.Services.Database;
using NadekoBot.Services.Database.Models;
using NLog;
using System;
@ -15,158 +15,139 @@ namespace NadekoBot.Modules.Administration
public partial class Administration
{
[Group]
public class ServerGreetCommands
public class ServerGreetCommands : ModuleBase
{
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)
//todo optimize ASAP
private static async void UserLeft(IGuildUser user)
{
var leftTask = Task.Run(async () =>
try
{
GuildConfig conf;
using (var uow = DbHandler.UnitOfWork())
{
conf = uow.GuildConfigs.For(user.Guild.Id, set => set);
}
if (!conf.SendChannelByeMessage) return;
var channel = (await user.Guild.GetTextChannelsAsync()).SingleOrDefault(c => c.Id == conf.ByeMessageChannelId);
if (channel == null) //maybe warn the server owner that the channel is missing
return;
var msg = conf.ChannelByeMessageText.Replace("%user%", user.Username).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
if (string.IsNullOrWhiteSpace(msg))
return;
try
{
GuildConfig conf;
using (var uow = DbHandler.UnitOfWork())
var toDelete = await channel.SendMessageAsync(msg.SanitizeMentions()).ConfigureAwait(false);
if (conf.AutoDeleteByeMessagesTimer > 0)
{
conf = uow.GuildConfigs.For(user.Guild.Id);
toDelete.DeleteAfter(conf.AutoDeleteByeMessagesTimer);
}
if (!conf.SendChannelByeMessage) return;
var channel = (await user.Guild.GetTextChannelsAsync()).SingleOrDefault(c => c.Id == conf.ByeMessageChannelId);
if (channel == null) //maybe warn the server owner that the channel is missing
return;
var msg = conf.ChannelByeMessageText.Replace("%user%", user.Username).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
if (string.IsNullOrWhiteSpace(msg))
return;
try
{
var toDelete = await channel.SendMessageAsync(msg.SanitizeMentions()).ConfigureAwait(false);
if (conf.AutoDeleteByeMessagesTimer > 0)
{
var t = Task.Run(async () =>
{
await Task.Delay(conf.AutoDeleteByeMessagesTimer * 1000).ConfigureAwait(false); // 5 minutes
try { await toDelete.DeleteAsync().ConfigureAwait(false); } catch { }
});
}
}
catch (Exception ex) { _log.Warn(ex); }
}
catch { }
});
return Task.CompletedTask;
catch (Exception ex) { _log.Warn(ex); }
}
catch { }
}
private Task UserJoined(IGuildUser user)
private static async void UserJoined(IGuildUser user)
{
var joinedTask = Task.Run(async () =>
try
{
try
GuildConfig conf;
using (var uow = DbHandler.UnitOfWork())
{
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)
if (conf.SendChannelGreetMessage)
{
var channel = (await user.Guild.GetTextChannelsAsync()).SingleOrDefault(c => c.Id == conf.GreetMessageChannelId);
if (channel != null) //maybe warn the server owner that the channel is missing
{
var channel = (await user.Guild.GetTextChannelsAsync()).SingleOrDefault(c => c.Id == conf.GreetMessageChannelId);
if (channel != null) //maybe warn the server owner that the channel is missing
var msg = conf.ChannelGreetMessageText.Replace("%user%", user.Mention).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
if (!string.IsNullOrWhiteSpace(msg))
{
var msg = conf.ChannelGreetMessageText.Replace("%user%", user.Mention).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
if (!string.IsNullOrWhiteSpace(msg))
try
{
try
var toDelete = await channel.SendMessageAsync(msg.SanitizeMentions()).ConfigureAwait(false);
if (conf.AutoDeleteGreetMessagesTimer > 0)
{
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); }
}
}
}
if (conf.SendDmGreetMessage)
{
var channel = await user.CreateDMChannelAsync();
if (channel != null)
{
var msg = conf.DmGreetMessageText.Replace("%user%", user.Username).Replace("%server%", user.Guild.Name);
if (!string.IsNullOrWhiteSpace(msg))
{
await channel.SendMessageAsync(msg).ConfigureAwait(false);
}
catch (Exception ex) { _log.Warn(ex); }
}
}
}
catch { }
});
return Task.CompletedTask;
if (conf.SendDmGreetMessage)
{
var channel = await user.CreateDMChannelAsync();
if (channel != null)
{
var msg = conf.DmGreetMessageText.Replace("%user%", user.Username).Replace("%server%", user.Guild.Name);
if (!string.IsNullOrWhiteSpace(msg))
{
await channel.SendConfirmAsync(msg).ConfigureAwait(false);
}
}
}
}
catch { }
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[RequirePermission(GuildPermission.ManageGuild)]
public async Task GreetDel(IUserMessage umsg, int timer = 30)
[RequireUserPermission(GuildPermission.ManageGuild)]
public async Task GreetDel(int timer = 30)
{
var channel = (ITextChannel)umsg.Channel;
var channel = (ITextChannel)Context.Channel;
if (timer < 0 || timer > 600)
return;
await ServerGreetCommands.SetGreetDel(channel.Guild.Id, timer).ConfigureAwait(false);
await ServerGreetCommands.SetGreetDel(Context.Guild.Id, timer).ConfigureAwait(false);
if (timer > 0)
await channel.SendMessageAsync($"🆗 Greet messages **will be deleted** after `{timer} seconds`.").ConfigureAwait(false);
await Context.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 Context.Channel.SendConfirmAsync(" Automatic deletion of greet messages has been **disabled**.").ConfigureAwait(false);
}
private static async Task SetGreetDel(ulong id, int timer)
{
if (timer < 0 || timer > 600)
return;
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);
}
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[RequirePermission(GuildPermission.ManageGuild)]
public async Task Greet(IUserMessage umsg)
[RequireUserPermission(GuildPermission.ManageGuild)]
public async Task Greet()
{
var channel = (ITextChannel)umsg.Channel;
var enabled = await ServerGreetCommands.SetGreet(channel.Guild.Id, channel.Id).ConfigureAwait(false);
var enabled = await ServerGreetCommands.SetGreet(Context.Guild.Id, Context.Channel.Id).ConfigureAwait(false);
if (enabled)
await channel.SendMessageAsync("✅ Greeting messages **enabled** on this channel.").ConfigureAwait(false);
await Context.Channel.SendConfirmAsync("✅ Greeting messages **enabled** on this channel.").ConfigureAwait(false);
else
await channel.SendMessageAsync(" Greeting messages **disabled**.").ConfigureAwait(false);
await Context.Channel.SendConfirmAsync(" Greeting messages **disabled**.").ConfigureAwait(false);
}
private static async Task<bool> SetGreet(ulong guildId, ulong channelId, bool? value = null)
@ -174,38 +155,36 @@ 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;
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[RequirePermission(GuildPermission.ManageGuild)]
public async Task GreetMsg(IUserMessage umsg, [Remainder] string text = null)
[RequireUserPermission(GuildPermission.ManageGuild)]
public async Task GreetMsg([Remainder] string text = null)
{
var channel = (ITextChannel)umsg.Channel;
if (string.IsNullOrWhiteSpace(text))
{
GuildConfig config;
string channelGreetMessageText;
using (var uow = DbHandler.UnitOfWork())
{
config = uow.GuildConfigs.For(channel.Guild.Id);
channelGreetMessageText = uow.GuildConfigs.For(Context.Guild.Id, set => set).ChannelGreetMessageText;
}
await channel.SendMessageAsync(" Current **greet** message: `" + config.ChannelGreetMessageText?.SanitizeMentions() + "`");
await Context.Channel.SendConfirmAsync("Current greet message: ", channelGreetMessageText?.SanitizeMentions());
return;
}
var sendGreetEnabled = ServerGreetCommands.SetGreetMessage(channel.Guild.Id, ref text);
var sendGreetEnabled = ServerGreetCommands.SetGreetMessage(Context.Guild.Id, ref text);
await channel.SendMessageAsync("🆗 New greet message **set**.").ConfigureAwait(false);
await Context.Channel.SendConfirmAsync("🆗 New greet message **set**.").ConfigureAwait(false);
if (!sendGreetEnabled)
await channel.SendMessageAsync(" Enable greet messsages by typing `.greet`").ConfigureAwait(false);
await Context.Channel.SendConfirmAsync(" Enable greet messsages by typing `.greet`").ConfigureAwait(false);
}
public static bool SetGreetMessage(ulong guildId, ref string message)
@ -218,11 +197,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;
@ -230,17 +208,15 @@ namespace NadekoBot.Modules.Administration
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[RequirePermission(GuildPermission.ManageGuild)]
public async Task GreetDm(IUserMessage umsg)
[RequireUserPermission(GuildPermission.ManageGuild)]
public async Task GreetDm()
{
var channel = (ITextChannel)umsg.Channel;
var enabled = await ServerGreetCommands.SetGreetDm(channel.Guild.Id).ConfigureAwait(false);
var enabled = await ServerGreetCommands.SetGreetDm(Context.Guild.Id).ConfigureAwait(false);
if (enabled)
await channel.SendMessageAsync("🆗 DM Greet announcements **enabled**.").ConfigureAwait(false);
await Context.Channel.SendConfirmAsync("🆗 DM Greet announcements **enabled**.").ConfigureAwait(false);
else
await channel.SendMessageAsync(" Greet announcements **disabled**.").ConfigureAwait(false);
await Context.Channel.SendConfirmAsync(" Greet announcements **disabled**.").ConfigureAwait(false);
}
private static async Task<bool> SetGreetDm(ulong guildId, bool? value = null)
@ -248,9 +224,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;
@ -258,27 +234,25 @@ namespace NadekoBot.Modules.Administration
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[RequirePermission(GuildPermission.ManageGuild)]
public async Task GreetDmMsg(IUserMessage umsg, [Remainder] string text = null)
[RequireUserPermission(GuildPermission.ManageGuild)]
public async Task GreetDmMsg([Remainder] string text = null)
{
var channel = (ITextChannel)umsg.Channel;
if (string.IsNullOrWhiteSpace(text))
{
GuildConfig config;
using (var uow = DbHandler.UnitOfWork())
{
config = uow.GuildConfigs.For(channel.Guild.Id);
config = uow.GuildConfigs.For(Context.Guild.Id);
}
await channel.SendMessageAsync(" Current **DM greet** message: `" + config.DmGreetMessageText?.SanitizeMentions() + "`");
await Context.Channel.SendConfirmAsync(" Current **DM greet** message: `" + config.DmGreetMessageText?.SanitizeMentions() + "`");
return;
}
var sendGreetEnabled = ServerGreetCommands.SetGreetDmMessage(channel.Guild.Id, ref text);
var sendGreetEnabled = ServerGreetCommands.SetGreetDmMessage(Context.Guild.Id, ref text);
await channel.SendMessageAsync("🆗 New DM greet message **set**.").ConfigureAwait(false);
await Context.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 Context.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 +269,6 @@ namespace NadekoBot.Modules.Administration
conf.DmGreetMessageText = message;
greetMsgEnabled = conf.SendDmGreetMessage;
uow.GuildConfigs.Update(conf);
uow.Complete();
}
return greetMsgEnabled;
@ -303,17 +276,15 @@ namespace NadekoBot.Modules.Administration
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[RequirePermission(GuildPermission.ManageGuild)]
public async Task Bye(IUserMessage umsg)
[RequireUserPermission(GuildPermission.ManageGuild)]
public async Task Bye()
{
var channel = (ITextChannel)umsg.Channel;
var enabled = await ServerGreetCommands.SetBye(channel.Guild.Id, channel.Id).ConfigureAwait(false);
var enabled = await ServerGreetCommands.SetBye(Context.Guild.Id, Context.Channel.Id).ConfigureAwait(false);
if (enabled)
await channel.SendMessageAsync("✅ Bye announcements **enabled** on this channel.").ConfigureAwait(false);
await Context.Channel.SendConfirmAsync("✅ Bye announcements **enabled** on this channel.").ConfigureAwait(false);
else
await channel.SendMessageAsync(" Bye announcements **disabled**.").ConfigureAwait(false);
await Context.Channel.SendConfirmAsync(" Bye announcements **disabled**.").ConfigureAwait(false);
}
private static async Task<bool> SetBye(ulong guildId, ulong channelId, bool? value = null)
@ -321,10 +292,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;
@ -332,29 +303,27 @@ namespace NadekoBot.Modules.Administration
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[RequirePermission(GuildPermission.ManageGuild)]
public async Task ByeMsg(IUserMessage umsg, [Remainder] string text = null)
[RequireUserPermission(GuildPermission.ManageGuild)]
public async Task ByeMsg([Remainder] string text = null)
{
var channel = (ITextChannel)umsg.Channel;
if (string.IsNullOrWhiteSpace(text))
{
GuildConfig config;
string byeMessageText;
using (var uow = DbHandler.UnitOfWork())
{
config = uow.GuildConfigs.For(channel.Guild.Id);
byeMessageText = uow.GuildConfigs.For(Context.Guild.Id, set => set).ChannelByeMessageText;
}
await channel.SendMessageAsync(" Current **bye** message: `" + config.ChannelByeMessageText?.SanitizeMentions() + "`");
await Context.Channel.SendConfirmAsync(" Current **bye** message: `" + byeMessageText?.SanitizeMentions() + "`");
return;
}
var sendByeEnabled = ServerGreetCommands.SetByeMessage(channel.Guild.Id, ref text);
var sendByeEnabled = ServerGreetCommands.SetByeMessage(Context.Guild.Id, ref text);
await channel.SendMessageAsync("🆗 New bye message **set**.").ConfigureAwait(false);
await Context.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 Context.Channel.SendConfirmAsync($" Enable bye messsages by typing `{NadekoBot.ModulePrefixes[typeof(Administration).Name]}bye`").ConfigureAwait(false);
}
public static bool SetByeMessage(ulong guildId, ref string message)
{
message = message?.SanitizeMentions();
@ -365,11 +334,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;
@ -377,17 +345,15 @@ namespace NadekoBot.Modules.Administration
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[RequirePermission(GuildPermission.ManageGuild)]
public async Task ByeDel(IUserMessage umsg, int timer = 30)
[RequireUserPermission(GuildPermission.ManageGuild)]
public async Task ByeDel(int timer = 30)
{
var channel = (ITextChannel)umsg.Channel;
await ServerGreetCommands.SetByeDel(channel.Guild.Id, timer).ConfigureAwait(false);
await ServerGreetCommands.SetByeDel(Context.Guild.Id, timer).ConfigureAwait(false);
if (timer > 0)
await channel.SendMessageAsync($"🆗 Bye messages **will be deleted** after `{timer} seconds`.").ConfigureAwait(false);
await Context.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 Context.Channel.SendConfirmAsync(" Automatic deletion of bye messages has been **disabled**.").ConfigureAwait(false);
}
private static async Task SetByeDel(ulong id, int timer)
@ -397,13 +363,13 @@ 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

@ -4,8 +4,10 @@ using Discord.WebSocket;
using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Services;
using NLog;
using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
@ -15,108 +17,109 @@ namespace NadekoBot.Modules.Administration
public partial class Administration
{
[Group]
public class VoicePlusTextCommands
public class VoicePlusTextCommands : ModuleBase
{
Regex channelNameRegex = new Regex(@"[^a-zA-Z0-9 -]", RegexOptions.Compiled);
private ConcurrentHashSet<ulong> voicePlusTextCache;
public VoicePlusTextCommands()
private static Regex channelNameRegex = new Regex(@"[^a-zA-Z0-9 -]", RegexOptions.Compiled);
private static ConcurrentHashSet<ulong> voicePlusTextCache { get; }
static VoicePlusTextCommands()
{
var _log = LogManager.GetCurrentClassLogger();
var sw = Stopwatch.StartNew();
using (var uow = DbHandler.UnitOfWork())
{
voicePlusTextCache = new ConcurrentHashSet<ulong>(NadekoBot.AllGuildConfigs.Where(g => g.VoicePlusTextEnabled).Select(g => g.GuildId));
}
NadekoBot.Client.UserVoiceStateUpdated += UserUpdatedEventHandler;
sw.Stop();
_log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s");
}
private Task UserUpdatedEventHandler(IUser iuser, IVoiceState before, IVoiceState after)
private static async void UserUpdatedEventHandler(SocketUser iuser, SocketVoiceState before, SocketVoiceState after)
{
var user = (iuser as IGuildUser);
var user = (iuser as SocketGuildUser);
var guild = user?.Guild;
if (guild == null)
return Task.CompletedTask;
var task = Task.Run(async () =>
return;
try
{
try
var botUserPerms = guild.CurrentUser.GuildPermissions;
if (before.VoiceChannel == after.VoiceChannel) return;
if (!voicePlusTextCache.Contains(guild.Id))
return;
if (!botUserPerms.ManageChannels || !botUserPerms.ManageRoles)
{
var botUserPerms = guild.GetCurrentUser().GuildPermissions;
if (before.VoiceChannel == after.VoiceChannel) return;
if (!voicePlusTextCache.Contains(guild.Id))
return;
if (!botUserPerms.ManageChannels || !botUserPerms.ManageRoles)
try
{
try
{
await (await guild.GetOwnerAsync()).SendMessageAsync(
"⚠️ 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;
voicePlusTextCache.TryRemove(guild.Id);
await uow.CompleteAsync().ConfigureAwait(false);
}
return;
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);
}
var beforeVch = before.VoiceChannel;
if (beforeVch != null)
catch { }
using (var uow = DbHandler.UnitOfWork())
{
var textChannel = guild.GetTextChannels().Where(t => t.Name == GetChannelName(beforeVch.Name).ToLowerInvariant()).FirstOrDefault();
if (textChannel != null)
await textChannel.AddPermissionOverwriteAsync(user,
new OverwritePermissions(readMessages: PermValue.Deny,
sendMessages: PermValue.Deny)).ConfigureAwait(false);
uow.GuildConfigs.For(guild.Id, set => set).VoicePlusTextEnabled = false;
voicePlusTextCache.TryRemove(guild.Id);
await uow.CompleteAsync().ConfigureAwait(false);
}
var afterVch = after.VoiceChannel;
if (afterVch != null && guild.AFKChannelId != afterVch.Id)
{
var textChannel = guild.GetTextChannels()
.Where(t => t.Name == GetChannelName(afterVch.Name).ToLowerInvariant())
.FirstOrDefault();
if (textChannel == null)
{
textChannel = (await guild.CreateTextChannelAsync(GetChannelName(afterVch.Name).ToLowerInvariant()).ConfigureAwait(false));
await textChannel.AddPermissionOverwriteAsync(guild.EveryoneRole,
new OverwritePermissions(readMessages: PermValue.Deny,
sendMessages: PermValue.Deny)).ConfigureAwait(false);
}
return;
}
var beforeVch = before.VoiceChannel;
if (beforeVch != null)
{
var textChannel = (await guild.GetTextChannelsAsync()).Where(t => t.Name == GetChannelName(beforeVch.Name).ToLowerInvariant()).FirstOrDefault();
if (textChannel != null)
await textChannel.AddPermissionOverwriteAsync(user,
new OverwritePermissions(readMessages: PermValue.Allow,
sendMessages: PermValue.Allow)).ConfigureAwait(false);
}
new OverwritePermissions(readMessages: PermValue.Deny,
sendMessages: PermValue.Deny)).ConfigureAwait(false);
}
catch (Exception ex)
var afterVch = after.VoiceChannel;
if (afterVch != null && guild.AFKChannelId != afterVch.Id)
{
Console.WriteLine(ex);
var textChannel = (await guild.GetTextChannelsAsync())
.Where(t => t.Name == GetChannelName(afterVch.Name).ToLowerInvariant())
.FirstOrDefault();
if (textChannel == null)
{
textChannel = (await guild.CreateTextChannelAsync(GetChannelName(afterVch.Name).ToLowerInvariant()).ConfigureAwait(false));
await textChannel.AddPermissionOverwriteAsync(guild.EveryoneRole,
new OverwritePermissions(readMessages: PermValue.Deny,
sendMessages: PermValue.Deny)).ConfigureAwait(false);
}
await textChannel.AddPermissionOverwriteAsync(user,
new OverwritePermissions(readMessages: PermValue.Allow,
sendMessages: PermValue.Allow)).ConfigureAwait(false);
}
});
return Task.CompletedTask;
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
private string GetChannelName(string voiceName) =>
private static string GetChannelName(string voiceName) =>
channelNameRegex.Replace(voiceName, "").Trim().Replace(" ", "-").TrimTo(90, true) + "-voice";
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[RequirePermission(GuildPermission.ManageRoles)]
[RequirePermission(GuildPermission.ManageChannels)]
public async Task VoicePlusText(IUserMessage msg)
[RequireUserPermission(GuildPermission.ManageRoles)]
[RequireUserPermission(GuildPermission.ManageChannels)]
public async Task VoicePlusText()
{
var channel = (ITextChannel)msg.Channel;
var guild = channel.Guild;
var guild = Context.Guild;
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 Context.Channel.SendErrorAsync("I require atleast **manage roles** and **manage channels permissions** to enable this feature. `(preffered Administration permission)`");
return;
}
@ -124,7 +127,7 @@ namespace NadekoBot.Modules.Administration
{
try
{
await channel.SendMessageAsync("⚠️ You are enabling this feature and **I do not have ADMINISTRATOR permissions**. " +
await Context.Channel.SendErrorAsync("⚠️ You are enabling this feature and **I do not have ADMINISTRATOR permissions**. " +
"`This may cause some issues, and you will have to clean up text channels yourself afterwards.`");
}
catch { }
@ -134,46 +137,45 @@ 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);
}
if (!isEnabled)
{
voicePlusTextCache.TryRemove(guild.Id);
foreach (var textChannel in guild.GetTextChannels().Where(c => c.Name.EndsWith("-voice")))
foreach (var textChannel in (await guild.GetTextChannelsAsync().ConfigureAwait(false)).Where(c => c.Name.EndsWith("-voice")))
{
try { await textChannel.DeleteAsync().ConfigureAwait(false); } catch { }
}
await channel.SendMessageAsync(" Successfuly **removed** voice + text feature.").ConfigureAwait(false);
await Context.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 Context.Channel.SendConfirmAsync("🆗 Successfuly **enabled** voice + text feature.").ConfigureAwait(false);
}
catch (Exception ex)
{
await channel.SendMessageAsync(ex.ToString()).ConfigureAwait(false);
await Context.Channel.SendErrorAsync(ex.ToString()).ConfigureAwait(false);
}
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[RequirePermission(GuildPermission.ManageChannels)]
[RequirePermission(GuildPermission.ManageRoles)]
public async Task CleanVPlusT(IUserMessage msg)
[RequireUserPermission(GuildPermission.ManageChannels)]
[RequireUserPermission(GuildPermission.ManageRoles)]
public async Task CleanVPlusT()
{
var channel = (ITextChannel)msg.Channel;
var guild = channel.Guild;
var guild = Context.Guild;
var botUser = await guild.GetCurrentUserAsync().ConfigureAwait(false);
if (!botUser.GuildPermissions.Administrator)
{
await channel.SendMessageAsync("⚠️ I need **Administrator permission** to do that.").ConfigureAwait(false);
await Context.Channel.SendErrorAsync("I need **Administrator permission** to do that.").ConfigureAwait(false);
return;
}
var allTxtChannels = guild.GetTextChannels().Where(c => c.Name.EndsWith("-voice"));
var validTxtChannelNames = guild.GetVoiceChannels().Select(c => GetChannelName(c.Name).ToLowerInvariant());
var allTxtChannels = (await guild.GetTextChannelsAsync()).Where(c => c.Name.EndsWith("-voice"));
var validTxtChannelNames = (await guild.GetVoiceChannelsAsync()).Select(c => GetChannelName(c.Name).ToLowerInvariant());
var invalidTxtChannels = allTxtChannels.Where(c => !validTxtChannelNames.Contains(c.Name));
@ -183,8 +185,8 @@ namespace NadekoBot.Modules.Administration
await Task.Delay(500);
}
await channel.SendMessageAsync("✅ Done.").ConfigureAwait(false);
await Context.Channel.SendConfirmAsync("Cleaned v+t.").ConfigureAwait(false);
}
}
}
}
}

View File

@ -7,9 +7,12 @@ using System.Threading.Tasks;
using Discord;
using NadekoBot.Services;
using NadekoBot.Attributes;
using Discord.WebSocket;
using NadekoBot.Services.Database.Models;
using System.Linq;
using NadekoBot.Extensions;
using System.Threading;
using System.Diagnostics;
using NLog;
namespace NadekoBot.Modules.ClashOfClans
{
@ -18,8 +21,14 @@ namespace NadekoBot.Modules.ClashOfClans
{
public static ConcurrentDictionary<ulong, List<ClashWar>> ClashWars { get; set; } = new ConcurrentDictionary<ulong, List<ClashWar>>();
private static Timer checkWarTimer { get; }
private static new readonly Logger _log;
static ClashOfClans()
{
_log = LogManager.GetCurrentClassLogger();
var sw = Stopwatch.StartNew();
using (var uow = DbHandler.UnitOfWork())
{
ClashWars = new ConcurrentDictionary<ulong, List<ClashWar>>(
@ -28,16 +37,26 @@ namespace NadekoBot.Modules.ClashOfClans
.Select(cw =>
{
cw.Channel = NadekoBot.Client.GetGuild(cw.GuildId)
?.GetTextChannel(cw.ChannelId);
?.GetTextChannelAsync(cw.ChannelId)
.GetAwaiter()
.GetResult();
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,22 +64,29 @@ 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)
{
Bases[i] = null;
try { await war.Channel.SendMessageAsync($"❗🔰**Claim from @{Bases[i].CallUser} for a war against {war.ShortPrint()} has expired.**").ConfigureAwait(false); } catch { }
}
if (Bases[i].Stars != 3)
Bases[i].BaseDestroyed = true;
else
Bases[i] = null;
try
{
SaveWar(war);
await war.Channel.SendErrorAsync($"❗🔰**Claim from @{Bases[i].CallUser} for a war against {war.ShortPrint()} has expired.**").ConfigureAwait(false);
}
catch { }
}
}
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task CreateWar(IUserMessage umsg, int size, [Remainder] string enemyClan = null)
public async Task CreateWar(int size, [Remainder] string enemyClan = null)
{
var channel = (ITextChannel)umsg.Channel;
if (!(umsg.Author as IGuildUser).GuildPermissions.ManageChannels)
if (!(Context.User as IGuildUser).GuildPermissions.ManageChannels)
return;
if (string.IsNullOrWhiteSpace(enemyClan))
@ -68,67 +94,64 @@ namespace NadekoBot.Modules.ClashOfClans
if (size < 10 || size > 50 || size % 5 != 0)
{
await channel.SendMessageAsync("💢🔰 Not a Valid war size").ConfigureAwait(false);
await Context.Channel.SendErrorAsync("🔰 Not a Valid war size").ConfigureAwait(false);
return;
}
List<ClashWar> wars;
if (!ClashWars.TryGetValue(channel.Guild.Id, out wars))
if (!ClashWars.TryGetValue(Context.Guild.Id, out wars))
{
wars = new List<ClashWar>();
if (!ClashWars.TryAdd(channel.Guild.Id, wars))
if (!ClashWars.TryAdd(Context.Guild.Id, wars))
return;
}
var cw = await CreateWar(enemyClan, size, channel.Guild.Id, umsg.Channel.Id);
var cw = await CreateWar(enemyClan, size, Context.Guild.Id, Context.Channel.Id);
wars.Add(cw);
await channel.SendMessageAsync($"❗🔰**CREATED CLAN WAR AGAINST {cw.ShortPrint()}**").ConfigureAwait(false);
await Context.Channel.SendConfirmAsync($"❗🔰**CREATED CLAN WAR AGAINST {cw.ShortPrint()}**").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task StartWar(IUserMessage umsg, [Remainder] string number = null)
public async Task StartWar([Remainder] string number = null)
{
var channel = (ITextChannel)umsg.Channel;
int num = 0;
int.TryParse(number, out num);
var warsInfo = GetWarInfo(umsg, num);
var warsInfo = GetWarInfo(Context.Guild, num);
if (warsInfo == null)
{
await channel.SendMessageAsync("💢🔰 **That war does not exist.**").ConfigureAwait(false);
await Context.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 Context.Channel.SendConfirmAsync($"🔰**STARTED WAR AGAINST {war.ShortPrint()}**").ConfigureAwait(false);
}
catch
{
await channel.SendMessageAsync($"🔰**WAR AGAINST {war.ShortPrint()} HAS ALREADY STARTED**").ConfigureAwait(false);
await Context.Channel.SendErrorAsync($"🔰**WAR AGAINST {war.ShortPrint()} HAS ALREADY STARTED**").ConfigureAwait(false);
}
SaveWar(war);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task ListWar(IUserMessage umsg, [Remainder] string number = null)
public async Task ListWar([Remainder] string number = null)
{
var channel = (ITextChannel)umsg.Channel;
// if number is null, print all wars in a short way
if (string.IsNullOrWhiteSpace(number))
{
//check if there are any wars
List<ClashWar> wars = null;
ClashWars.TryGetValue(channel.Guild.Id, out wars);
ClashWars.TryGetValue(Context.Guild.Id, out wars);
if (wars == null || wars.Count == 0)
{
await channel.SendMessageAsync("🔰 **No active wars.**").ConfigureAwait(false);
await Context.Channel.SendErrorAsync("🔰 **No active wars.**").ConfigureAwait(false);
return;
}
@ -141,90 +164,84 @@ 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 Context.Channel.SendConfirmAsync(sb.ToString()).ConfigureAwait(false);
return;
}
var num = 0;
int.TryParse(number, out num);
//if number is not null, print the war needed
var warsInfo = GetWarInfo(umsg, num);
var warsInfo = GetWarInfo(Context.Guild, num);
if (warsInfo == null)
{
await channel.SendMessageAsync("💢🔰 **That war does not exist.**").ConfigureAwait(false);
await Context.Channel.SendErrorAsync("🔰 **That war does not exist.**").ConfigureAwait(false);
return;
}
await channel.SendMessageAsync(warsInfo.Item1[warsInfo.Item2].ToPrettyString()).ConfigureAwait(false);
await Context.Channel.SendConfirmAsync(warsInfo.Item1[warsInfo.Item2].ToPrettyString()).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Claim(IUserMessage umsg, int number, int baseNumber, [Remainder] string other_name = null)
public async Task Claim(int number, int baseNumber, [Remainder] string other_name = null)
{
var channel = (ITextChannel)umsg.Channel;
var warsInfo = GetWarInfo(umsg, number);
var warsInfo = GetWarInfo(Context.Guild, number);
if (warsInfo == null || warsInfo.Item1.Count == 0)
{
await channel.SendMessageAsync("💢🔰 **That war does not exist.**").ConfigureAwait(false);
await Context.Channel.SendErrorAsync("🔰 **That war does not exist.**").ConfigureAwait(false);
return;
}
var usr =
string.IsNullOrWhiteSpace(other_name) ?
umsg.Author.Username :
Context.User.Username :
other_name;
try
{
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 Context.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 Context.Channel.SendErrorAsync($"🔰 {ex.Message}").ConfigureAwait(false);
}
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task ClaimFinish1(IUserMessage umsg, int number, int baseNumber = 0)
public async Task ClaimFinish1(int number, int baseNumber = 0)
{
var channel = (ITextChannel)umsg.Channel;
await FinishClaim(umsg, number, baseNumber - 1, 1);
await FinishClaim(number, baseNumber - 1, 1);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task ClaimFinish2(IUserMessage umsg, int number, int baseNumber = 0)
public async Task ClaimFinish2(int number, int baseNumber = 0)
{
var channel = (ITextChannel)umsg.Channel;
await FinishClaim(umsg, number, baseNumber - 1, 2);
await FinishClaim(number, baseNumber - 1, 2);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task ClaimFinish(IUserMessage umsg, int number, int baseNumber = 0)
public async Task ClaimFinish(int number, int baseNumber = 0)
{
var channel = (ITextChannel)umsg.Channel;
await FinishClaim(umsg, number, baseNumber - 1);
await FinishClaim(number, baseNumber - 1);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task EndWar(IUserMessage umsg, int number)
public async Task EndWar(int number)
{
var channel = (ITextChannel)umsg.Channel;
var warsInfo = GetWarInfo(umsg,number);
var warsInfo = GetWarInfo(Context.Guild, number);
if (warsInfo == null)
{
await channel.SendMessageAsync("💢🔰 That war does not exist.").ConfigureAwait(false);
await Context.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 Context.Channel.SendConfirmAsync($"❗🔰**War against {warsInfo.Item1[warsInfo.Item2].ShortPrint()} ended.**").ConfigureAwait(false);
var size = warsInfo.Item1[warsInfo.Item2].Size;
warsInfo.Item1.RemoveAt(warsInfo.Item2);
@ -232,40 +249,37 @@ namespace NadekoBot.Modules.ClashOfClans
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Unclaim(IUserMessage umsg, int number, [Remainder] string otherName = null)
public async Task Unclaim(int number, [Remainder] string otherName = null)
{
var channel = (ITextChannel)umsg.Channel;
var warsInfo = GetWarInfo(umsg, number);
var warsInfo = GetWarInfo(Context.Guild, number);
if (warsInfo == null || warsInfo.Item1.Count == 0)
{
await channel.SendMessageAsync("💢🔰 **That war does not exist.**").ConfigureAwait(false);
await Context.Channel.SendErrorAsync("🔰 **That war does not exist.**").ConfigureAwait(false);
return;
}
var usr =
string.IsNullOrWhiteSpace(otherName) ?
umsg.Author.Username :
Context.User.Username :
otherName;
try
{
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 Context.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 Context.Channel.SendErrorAsync($"🔰 {ex.Message}").ConfigureAwait(false);
}
}
private async Task FinishClaim(IUserMessage umsg, int number, int baseNumber, int stars = 3)
private async Task FinishClaim(int number, int baseNumber, int stars = 3)
{
var channel = (ITextChannel)umsg.Channel;
var warInfo = GetWarInfo(umsg, number);
var warInfo = GetWarInfo(Context.Guild, number);
if (warInfo == null || warInfo.Item1.Count == 0)
{
await channel.SendMessageAsync("💢🔰 **That war does not exist.**").ConfigureAwait(false);
await Context.Channel.SendErrorAsync("🔰 **That war does not exist.**").ConfigureAwait(false);
return;
}
var war = warInfo.Item1[warInfo.Item2];
@ -273,27 +287,25 @@ namespace NadekoBot.Modules.ClashOfClans
{
if (baseNumber == -1)
{
baseNumber = war.FinishClaim(umsg.Author.Username, stars);
baseNumber = war.FinishClaim(Context.User.Username, stars);
SaveWar(war);
}
else
{
war.FinishClaim(baseNumber, stars);
}
await channel.SendMessageAsync($"❗🔰{umsg.Author.Mention} **DESTROYED** a base #{baseNumber + 1} in a war against {war.ShortPrint()}").ConfigureAwait(false);
await Context.Channel.SendConfirmAsync($"❗🔰{Context.User.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 Context.Channel.SendErrorAsync($"🔰 {ex.Message}").ConfigureAwait(false);
}
}
private static Tuple<List<ClashWar>, int> GetWarInfo(IUserMessage umsg, int num)
private static Tuple<List<ClashWar>, int> GetWarInfo(IGuild guild, int num)
{
var channel = (ITextChannel)umsg.Channel;
//check if there are any wars
List<ClashWar> wars = null;
ClashWars.TryGetValue(channel.Guild.Id, out wars);
ClashWars.TryGetValue(guild.Id, out wars);
if (wars == null || wars.Count == 0)
{
return null;
@ -310,6 +322,7 @@ namespace NadekoBot.Modules.ClashOfClans
public static async Task<ClashWar> CreateWar(string enemyClan, int size, ulong serverId, ulong channelId)
{
var channel = await NadekoBot.Client.GetGuild(serverId)?.GetTextChannelAsync(channelId);
using (var uow = DbHandler.UnitOfWork())
{
var cw = new ClashWar
@ -319,8 +332,7 @@ namespace NadekoBot.Modules.ClashOfClans
Bases = new List<ClashCaller>(size),
GuildId = serverId,
ChannelId = channelId,
Channel = NadekoBot.Client.GetGuild(serverId)
?.GetTextChannel(channelId)
Channel = channel,
};
cw.Bases.Capacity = size;
for (int i = 0; i < size; i++)
@ -358,4 +370,4 @@ namespace NadekoBot.Modules.ClashOfClans
}
}
}
}
}

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,7 +95,14 @@ namespace NadekoBot.Modules.ClashOfClans
else
{
var left = (cw.WarState == StateOfWar.Started) ? twoHours - (DateTime.UtcNow - cw.Bases[i].TimeAdded) : twoHours;
sb.AppendLine($"`{i + 1}.` ✅ `{cw.Bases[i].CallUser}` {left.Hours}h {left.Minutes}m {left.Seconds}s left");
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

@ -7,47 +7,62 @@ using System.Collections.Concurrent;
using NadekoBot.Services.Database.Models;
using Discord;
using NadekoBot.Extensions;
using NLog;
using System.Diagnostics;
using Discord.WebSocket;
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>();
private static new readonly Logger _log;
static CustomReactions()
{
_log = LogManager.GetCurrentClassLogger();
var sw = Stopwatch.StartNew();
using (var uow = DbHandler.UnitOfWork())
{
var items = uow.CustomReactions.GetAll();
GuildReactions = new ConcurrentDictionary<ulong, ConcurrentHashSet<CustomReaction>>(items.Where(g => g.GuildId != null && g.GuildId != 0).GroupBy(k => k.GuildId.Value).ToDictionary(g => g.Key, g => new ConcurrentHashSet<CustomReaction>(g)));
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)
{
sw.Stop();
_log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s");
}
public static async Task<bool> TryExecuteCustomReaction(IUserMessage umsg)
public void ClearStats() => ReactionStats.Clear();
public static async Task<bool> TryExecuteCustomReaction(SocketUserMessage umsg)
{
var channel = umsg.Channel as ITextChannel;
var channel = umsg.Channel as SocketTextChannel;
if (channel == null)
return false;
var content = umsg.Content.Trim().ToLowerInvariant();
ConcurrentHashSet<CustomReaction> reactions;
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)
{
try { await channel.SendMessageAsync(reaction.ResponseWithContext(umsg)).ConfigureAwait(false); } catch { }
if (reaction.Response != "-")
try { await channel.SendMessageAsync(reaction.ResponseWithContext(umsg)).ConfigureAwait(false); } catch { }
ReactionStats.AddOrUpdate(reaction.Trigger, 1, (k, old) => ++old);
return true;
}
}
@ -61,23 +76,24 @@ 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;
}
[NadekoCommand, Usage, Description, Aliases]
public async Task AddCustReact(IUserMessage imsg, string key, [Remainder] string message)
public async Task AddCustReact(string key, [Remainder] string message)
{
var channel = imsg.Channel as ITextChannel;
var channel = Context.Channel as ITextChannel;
if (string.IsNullOrWhiteSpace(message) || string.IsNullOrWhiteSpace(key))
return;
key = key.ToLowerInvariant();
if ((channel == null && !NadekoBot.Credentials.IsOwner(imsg.Author)) || (channel != null && !((IGuildUser)imsg.Author).GuildPermissions.Administrator))
if ((channel == null && !NadekoBot.Credentials.IsOwner(Context.User)) || (channel != null && !((IGuildUser)Context.User).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 Context.Channel.SendErrorAsync("Insufficient permissions. Requires Bot ownership for global custom reactions, and Administrator for guild custom reactions."); } catch { }
return;
}
@ -102,38 +118,40 @@ namespace NadekoBot.Modules.CustomReactions
}
else
{
var reactions = GuildReactions.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet<CustomReaction>());
var reactions = GuildReactions.GetOrAdd(Context.Guild.Id, new ConcurrentHashSet<CustomReaction>());
reactions.Add(cr);
}
await imsg.Channel.SendMessageAsync($"`Added new custom reaction {cr.Id}:`\n\t`Trigger:` {key}\n\t`Response:` {message}").ConfigureAwait(false);
await Context.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))
).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[Priority(0)]
public async Task ListCustReact(IUserMessage imsg, int page = 1)
public async Task ListCustReact(int page = 1)
{
var channel = imsg.Channel as ITextChannel;
if (page < 1 || page > 1000)
return;
ConcurrentHashSet<CustomReaction> customReactions;
if (channel == null)
if (Context.Guild == null)
customReactions = GlobalReactions;
else
customReactions = GuildReactions.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet<CustomReaction>());
customReactions = GuildReactions.GetOrAdd(Context.Guild.Id, new ConcurrentHashSet<CustomReaction>());
if (customReactions == null || !customReactions.Any())
await imsg.Channel.SendMessageAsync("`No custom reactions found`").ConfigureAwait(false);
await Context.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)
.Skip((page - 1) * 20)
.Take(20)
.Select(cr => $"`#{cr.Id}` `Trigger:` {cr.Trigger}")))
.ConfigureAwait(false);
await Context.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}")))
.ConfigureAwait(false);
}
public enum All
@ -143,51 +161,48 @@ namespace NadekoBot.Modules.CustomReactions
[NadekoCommand, Usage, Description, Aliases]
[Priority(1)]
public async Task ListCustReact(IUserMessage imsg, All x)
public async Task ListCustReact(All x)
{
var channel = imsg.Channel as ITextChannel;
ConcurrentHashSet<CustomReaction> customReactions;
if (channel == null)
if (Context.Guild == null)
customReactions = GlobalReactions;
else
customReactions = GuildReactions.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet<CustomReaction>());
customReactions = GuildReactions.GetOrAdd(Context.Guild.Id, new ConcurrentHashSet<CustomReaction>());
if (customReactions == null || !customReactions.Any())
await imsg.Channel.SendMessageAsync("`No custom reactions found`").ConfigureAwait(false);
await Context.Channel.SendErrorAsync("No custom reactions found").ConfigureAwait(false);
else
{
var txtStream = await customReactions.GroupBy(cr => cr.Trigger)
.OrderBy(cr => cr.Key)
.Select(cr => new { Trigger = cr.Key, Responses = cr.Select(y => 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);
if (channel == null) // its a private one, just send back
await imsg.Channel.SendFileAsync(txtStream, "customreactions.txt", "List of all custom reactions").ConfigureAwait(false);
if (Context.Guild == null) // its a private one, just send back
await Context.Channel.SendFileAsync(txtStream, "customreactions.txt", "List of all custom reactions").ConfigureAwait(false);
else
await ((IGuildUser)imsg.Author).SendFileAsync(txtStream, "customreactions.txt", "List of all custom reactions").ConfigureAwait(false);
await ((IGuildUser)Context.User).SendFileAsync(txtStream, "customreactions.txt", "List of all custom reactions").ConfigureAwait(false);
}
}
[NadekoCommand, Usage, Description, Aliases]
public async Task ListCustReactG(IUserMessage imsg, int page = 1)
public async Task ListCustReactG(int page = 1)
{
var channel = imsg.Channel as ITextChannel;
if (page < 1 || page > 10000)
return;
ConcurrentHashSet<CustomReaction> customReactions;
if (channel == null)
if (Context.Guild == null)
customReactions = GlobalReactions;
else
customReactions = GuildReactions.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet<CustomReaction>());
customReactions = GuildReactions.GetOrAdd(Context.Guild.Id, new ConcurrentHashSet<CustomReaction>());
if (customReactions == null || !customReactions.Any())
await imsg.Channel.SendMessageAsync("`No custom reactions found`").ConfigureAwait(false);
await Context.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 Context.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)
@ -196,35 +211,34 @@ namespace NadekoBot.Modules.CustomReactions
}
[NadekoCommand, Usage, Description, Aliases]
public async Task ShowCustReact(IUserMessage imsg, int id)
public async Task ShowCustReact(int id)
{
var channel = imsg.Channel as ITextChannel;
ConcurrentHashSet<CustomReaction> customReactions;
if (channel == null)
if (Context.Guild == null)
customReactions = GlobalReactions;
else
customReactions = GuildReactions.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet<CustomReaction>());
customReactions = GuildReactions.GetOrAdd(Context.Guild.Id, new ConcurrentHashSet<CustomReaction>());
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 Context.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 Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
.WithDescription($"#{id}")
.AddField(efb => efb.WithName("Trigger").WithValue(found.Trigger))
.AddField(efb => efb.WithName("Response").WithValue(found.Response + "\n```css\n" + found.Response + "```"))
).ConfigureAwait(false);
}
}
[NadekoCommand, Usage, Description, Aliases]
public async Task DelCustReact(IUserMessage imsg, int id)
public async Task DelCustReact(int id)
{
var channel = imsg.Channel as ITextChannel;
if ((channel == null && !NadekoBot.Credentials.IsOwner(imsg.Author)) || (channel != null && !((IGuildUser)imsg.Author).GuildPermissions.Administrator))
if ((Context.Guild == null && !NadekoBot.Credentials.IsOwner(Context.User)) || (Context.Guild != null && !((IGuildUser)Context.User).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 Context.Channel.SendErrorAsync("Insufficient permissions. Requires Bot ownership for global custom reactions, and Administrator for guild custom reactions."); } catch { }
return;
}
@ -236,26 +250,63 @@ namespace NadekoBot.Modules.CustomReactions
if (toDelete == null) //not found
return;
if ((toDelete.GuildId == null || toDelete.GuildId == 0) && channel == null)
if ((toDelete.GuildId == null || toDelete.GuildId == 0) && Context.Guild == null)
{
uow.CustomReactions.Remove(toDelete);
GlobalReactions.RemoveWhere(cr => cr.Id == toDelete.Id);
success = true;
}
else if ((toDelete.GuildId != null && toDelete.GuildId != 0) && channel?.Guild.Id == toDelete.GuildId)
else if ((toDelete.GuildId != null && toDelete.GuildId != 0) && Context.Guild.Id == toDelete.GuildId)
{
uow.CustomReactions.Remove(toDelete);
GuildReactions.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet<CustomReaction>()).RemoveWhere(cr => cr.Id == toDelete.Id);
GuildReactions.GetOrAdd(Context.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 Context.Channel.SendConfirmAsync("Deleted custom reaction", toDelete.ToString()).ConfigureAwait(false);
else
await imsg.Channel.SendMessageAsync("`Failed to find that custom reaction.`").ConfigureAwait(false);
await Context.Channel.SendErrorAsync("Failed to find that custom reaction.").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[OwnerOnly]
public async Task CrStatsClear(string trigger = null)
{
if (string.IsNullOrWhiteSpace(trigger))
{
ClearStats();
await Context.Channel.SendConfirmAsync($"Custom reaction stats cleared.").ConfigureAwait(false);
}
else
{
uint throwaway;
if (ReactionStats.TryRemove(trigger, out throwaway))
{
await Context.Channel.SendConfirmAsync($"Stats cleared for `{trigger}` custom reaction.").ConfigureAwait(false);
}
else
{
await Context.Channel.SendErrorAsync("No stats for that trigger found, no action taken.").ConfigureAwait(false);
}
}
}
[NadekoCommand, Usage, Description, Aliases]
public async Task CrStats(int page = 1)
{
if (page < 1)
return;
await Context.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)))
)
.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
{
@ -15,9 +18,43 @@ namespace NadekoBot.Modules.CustomReactions
public static Dictionary<string, Func<IUserMessage, string>> placeholders = new Dictionary<string, Func<IUserMessage, string>>()
{
{"%mention%", (ctx) => { return $"<@{NadekoBot.Client.GetCurrentUser().Id}>"; } },
{"%mention%", (ctx) => { return $"<@{NadekoBot.Client.CurrentUser().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

@ -1,18 +1,14 @@
using Discord.Commands;
using NadekoBot.Services;
using NLog;
namespace NadekoBot.Modules
{
public class DiscordModule
public abstract class DiscordModule : ModuleBase
{
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 +16,6 @@ namespace NadekoBot.Modules
else
_prefix = "?missing_prefix?";
_l = loc;
_commands = cmds;
_client = client;
_log = LogManager.GetCurrentClassLogger();
}
}

View File

@ -1,5 +1,6 @@
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Services;
@ -16,43 +17,36 @@ namespace NadekoBot.Modules.Gambling
public partial class Gambling
{
[Group]
public class AnimalRacing
public class AnimalRacing : ModuleBase
{
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)]
public async Task Race(IUserMessage umsg)
public async Task Race()
{
var channel = (ITextChannel)umsg.Channel;
var ar = new AnimalRace(channel.Guild.Id, channel);
var ar = new AnimalRace(Context.Guild.Id, (ITextChannel)Context.Channel);
if (ar.Fail)
await channel.SendMessageAsync("🏁 `Failed starting a race. Another race is probably running.`");
await Context.Channel.SendErrorAsync("🏁 `Failed starting a race. Another race is probably running.`").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task JoinRace(IUserMessage umsg, int amount = 0)
public async Task JoinRace(int amount = 0)
{
var channel = (ITextChannel)umsg.Channel;
if (amount < 0)
amount = 0;
AnimalRace ar;
if (!AnimalRaces.TryGetValue(channel.Guild.Id, out ar))
if (!AnimalRaces.TryGetValue(Context.Guild.Id, out ar))
{
await channel.SendMessageAsync("No race exists on this server");
await Context.Channel.SendErrorAsync("No race exists on this server").ConfigureAwait(false);
return;
}
await ar.JoinRace(umsg.Author as IGuildUser, amount);
await ar.JoinRace(Context.User as IGuildUser, amount);
}
public class AnimalRace
@ -94,21 +88,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 +121,7 @@ namespace NadekoBot.Modules.Gambling
await Task.Run(StartRace);
End();
}
catch { }
catch { try { End(); } catch { } }
});
}
@ -145,19 +147,26 @@ namespace NadekoBot.Modules.Gambling
participants.ForEach(p =>
{
p.Total += 1 + rng.Next(0, 10);
if (p.Total > 60)
{
p.Total = 60;
if (winner == null)
{
winner = p;
}
if (p.Place == 0)
p.Place = place++;
}
});
participants
.OrderByDescending(p => p.Total)
.ForEach(p =>
{
if (p.Total > 60)
{
if (winner == null)
{
winner = p;
}
p.Total = 60;
if (p.Place == 0)
p.Place = place++;
}
});
//draw the state
var text = $@"|🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🔚|
@ -187,26 +196,26 @@ namespace NadekoBot.Modules.Gambling
if (winner.AmountBet > 0)
{
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(SocketMessage imsg)
{
var msg = imsg as IUserMessage;
var msg = imsg as SocketUserMessage;
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 +231,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 +275,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

@ -10,23 +10,23 @@ using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Image = ImageSharp.Image;
namespace NadekoBot.Modules.Gambling
{
public partial class Gambling
{
[Group]
public class DriceRollCommands
public class DriceRollCommands : ModuleBase
{
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)]
public async Task Roll(IUserMessage umsg)
public async Task Roll()
{
var channel = (ITextChannel)umsg.Channel;
if (channel == null)
return;
var rng = new NadekoRandom();
var gen = rng.Next(1, 101);
@ -44,59 +44,49 @@ namespace NadekoBot.Modules.Gambling
catch { return new MemoryStream(); }
});
await channel.SendFileAsync(imageStream, "dice.png", $"{umsg.Author.Mention} rolled " + Format.Code(gen.ToString())).ConfigureAwait(false);
await Context.Channel.SendFileAsync(imageStream, "dice.png", $"{Context.User.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)
public enum RollOrderType
{
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)
{
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)
public async Task Roll(int num)
{
var channel = (ITextChannel)umsg.Channel;
if (channel == null)
return;
await InternalRoll(num, true).ConfigureAwait(false);
}
var ordered = true;
[NadekoCommand, Usage, Description, Aliases]
[Priority(0)]
public async Task Rolluo(int num)
{
await InternalRoll(num, false).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[Priority(1)]
public async Task Roll(string arg)
{
await InternallDndRoll(arg, true).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[Priority(1)]
public async Task Rolluo(string arg)
{
await InternallDndRoll(arg, false).ConfigureAwait(false);
}
private async Task InternalRoll( int num, bool ordered)
{
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 Context.Channel.SendErrorAsync("Invalid number specified. You can roll up to 1-30 dice at a time.").ConfigureAwait(false);
return;
}
@ -134,24 +124,34 @@ namespace NadekoBot.Modules.Gambling
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);
await Context.Channel.SendFileAsync(ms, "dice.png", $"{Context.User.Mention} rolled {values.Count} {(values.Count == 1 ? "die" : "dice")}. Total: **{values.Sum()}** Average: **{(values.Sum() / (1.0f * values.Count)).ToString("N2")}**").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Rolluo(IUserMessage umsg, string arg)
private async Task InternallDndRoll(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)
{
int n1;
int n2;
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($"{Context.User.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 Context.Channel.EmbedAsync(embed).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)
@ -164,73 +164,23 @@ namespace NadekoBot.Modules.Gambling
var arr = new int[n1];
for (int i = 0; i < n1; i++)
{
arr[i] = rng.Next(1, n2 + 1) + add - sub;
arr[i] = rng.Next(1, n2 + 1);
}
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);
var sum = arr.Sum();
var embed = new EmbedBuilder().WithOkColor().WithDescription($"{Context.User.Mention} rolled {n1} {(n1 == 1 ? "die" : "dice")} `1 to {n2}`")
.AddField(efb => efb.WithName(Format.Bold("Rolls"))
.WithValue(string.Join(" ", (ordered ? arr.OrderBy(x => x).AsEnumerable() : arr).Select(x => Format.Code(x.ToString())))))
.AddField(efb => efb.WithName(Format.Bold("Sum"))
.WithValue(sum + " + " + add + " - " + sub + " = " + (sum + add - sub)));
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
}
}
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Rolluo(IUserMessage umsg, int num)
public async Task NRoll([Remainder] string range)
{
var channel = (ITextChannel)umsg.Channel;
if (channel == null)
return;
var ordered = false;
if (num < 1 || num > 30)
{
await channel.SendMessageAsync("Invalid number specified. You can roll up to 1-30 dice at a time.").ConfigureAwait(false);
return;
}
var rng = new NadekoRandom();
var dice = new List<Image>(num);
var values = new List<int>(num);
for (var i = 0; i < num; i++)
{
var randomNumber = rng.Next(1, 7);
var toInsert = dice.Count;
if (ordered)
{
if (randomNumber == 6 || dice.Count == 0)
toInsert = 0;
else if (randomNumber != 1)
for (var j = 0; j < dice.Count; j++)
{
if (values[j] < randomNumber)
{
toInsert = j;
break;
}
}
}
else
{
toInsert = dice.Count;
}
dice.Insert(toInsert, GetDice(randomNumber));
values.Insert(toInsert, randomNumber);
}
var bitmap = dice.Merge();
var ms = new MemoryStream();
bitmap.SaveAsPng(ms);
ms.Position = 0;
await channel.SendFileAsync(ms, "dice.png", $"{umsg.Author.Mention} rolled {values.Count} {(values.Count == 1 ? "die" : "dice")}. Total: **{values.Sum()}** Average: **{(values.Sum() / (1.0f * values.Count)).ToString("N2")}**").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task NRoll(IUserMessage umsg, [Remainder] string range)
{
var channel = (ITextChannel)umsg.Channel;
try
{
int rolled;
@ -241,7 +191,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 +199,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 Context.Channel.SendConfirmAsync($"{Context.User.Mention} rolled **{rolled}**.").ConfigureAwait(false);
}
catch (Exception ex)
{
await channel.SendMessageAsync($":anger: {ex.Message}").ConfigureAwait(false);
await Context.Channel.SendErrorAsync($":anger: {ex.Message}").ConfigureAwait(false);
}
}

View File

@ -10,31 +10,24 @@ using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Image = ImageSharp.Image;
namespace NadekoBot.Modules.Gambling
{
public partial class Gambling
{
[Group]
public class DrawCommands
public class DrawCommands : ModuleBase
{
private static readonly ConcurrentDictionary<IGuild, Cards> AllDecks = new ConcurrentDictionary<IGuild, Cards>();
public DrawCommands()
{
_log = LogManager.GetCurrentClassLogger();
}
private const string cardsPath = "data/images/cards";
private Logger _log { get; }
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Draw(IUserMessage msg, int num = 1)
public async Task Draw(int num = 1)
{
var channel = (ITextChannel)msg.Channel;
var cards = AllDecks.GetOrAdd(channel.Guild, (s) => new Cards());
var cards = AllDecks.GetOrAdd(Context.Guild, (s) => new Cards());
var images = new List<Image>();
var cardObjects = new List<Cards.Card>();
if (num > 5) num = 5;
@ -42,7 +35,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 Context.Channel.SendErrorAsync("No more cards in a deck.").ConfigureAwait(false); } catch { }
break;
}
var currentCard = cards.DrawACard();
@ -53,21 +46,20 @@ namespace NadekoBot.Modules.Gambling
MemoryStream bitmapStream = new MemoryStream();
images.Merge().SaveAsPng(bitmapStream);
bitmapStream.Position = 0;
//todo CARD NAMES?
var toSend = $"{msg.Author.Mention}";
var toSend = $"{Context.User.Mention}";
if (cardObjects.Count == 5)
toSend += $" drew `{Cards.GetHandValue(cardObjects)}`";
await channel.SendFileAsync(bitmapStream, images.Count + " cards.jpg", toSend).ConfigureAwait(false);
await Context.Channel.SendFileAsync(bitmapStream, images.Count + " cards.jpg", toSend).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task ShuffleDeck(IUserMessage imsg)
public async Task ShuffleDeck()
{
var channel = (ITextChannel)imsg.Channel;
//var channel = (ITextChannel)Context.Channel;
AllDecks.AddOrUpdate(channel.Guild,
AllDecks.AddOrUpdate(Context.Guild,
(g) => new Cards(),
(g, c) =>
{
@ -75,7 +67,7 @@ namespace NadekoBot.Modules.Gambling
return c;
});
await channel.SendMessageAsync("`Deck reshuffled.`").ConfigureAwait(false);
await Context.Channel.SendConfirmAsync("Deck reshuffled.").ConfigureAwait(false);
}
}
}

View File

@ -7,35 +7,33 @@ using NadekoBot.Services;
using System;
using System.IO;
using System.Threading.Tasks;
using Image = ImageSharp.Image;
namespace NadekoBot.Modules.Gambling
{
public partial class Gambling
{
[Group]
public class FlipCoinCommands
public class FlipCoinCommands : ModuleBase
{
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)]
public async Task Flip(IUserMessage imsg, int count = 1)
public async Task Flip(int count = 1)
{
var channel = (ITextChannel)imsg.Channel;
if (count == 1)
{
if (rng.Next(0, 2) == 1)
await channel.SendFileAsync(headsPath, $"{imsg.Author.Mention} rolled " + Format.Code("Heads") + ".").ConfigureAwait(false);
await Context.Channel.SendFileAsync(File.Open(headsPath, FileMode.OpenOrCreate), "heads.jpg", $"{Context.User.Mention} flipped " + Format.Code("Heads") + ".").ConfigureAwait(false);
else
await channel.SendFileAsync(tailsPath, $"{imsg.Author.Mention} rolled " + Format.Code("Tails") + ".").ConfigureAwait(false);
await Context.Channel.SendFileAsync(File.Open(tailsPath, FileMode.OpenOrCreate), "tails.jpg", $"{Context.User.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 Context.Channel.SendErrorAsync("`Invalid number specified. You can flip 1 to 10 coins.`");
return;
}
var imgs = new Image[count];
@ -45,39 +43,28 @@ namespace NadekoBot.Modules.Gambling
new Image(File.OpenRead(headsPath)) :
new Image(File.OpenRead(tailsPath));
}
await channel.SendFileAsync(imgs.Merge().ToStream(), $"{count} coins.png").ConfigureAwait(false);
await Context.Channel.SendFileAsync(imgs.Merge().ToStream(), $"{count} coins.png").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Betflip(IUserMessage umsg, int amount, string guess)
public async Task Betflip(int amount, string guess)
{
var channel = (ITextChannel)umsg.Channel;
var guildUser = (IGuildUser)umsg.Author;
var guessStr = guess.Trim().ToUpperInvariant();
if (guessStr != "H" && guessStr != "T" && guessStr != "HEADS" && guessStr != "TAILS")
return;
if (amount < 3)
{
await channel.SendMessageAsync($"You can't bet less than 3{Gambling.CurrencySign}.")
await Context.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(Context.User, "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 Context.Channel.SendErrorAsync($"{Context.User.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
@ -98,15 +85,15 @@ namespace NadekoBot.Modules.Gambling
if (isHeads == result)
{
var toWin = (int)Math.Round(amount * 1.8);
str = $"{umsg.Author.Mention}`You guessed it!` You won {toWin}{Gambling.CurrencySign}";
await CurrencyHandler.AddCurrencyAsync((IGuildUser)umsg.Author, "Betflip Gamble", toWin, false).ConfigureAwait(false);
str = $"{Context.User.Mention}`You guessed it!` You won {toWin}{Gambling.CurrencySign}";
await CurrencyHandler.AddCurrencyAsync(Context.User, "Betflip Gamble", toWin, false).ConfigureAwait(false);
}
else
{
str = $"{umsg.Author.Mention}`Better luck next time.`";
str = $"{Context.User.Mention}`Better luck next time.`";
}
await channel.SendFileAsync(imgPathToSend, str).ConfigureAwait(false);
await Context.Channel.SendFileAsync(File.Open(imgPathToSend, FileMode.OpenOrCreate), "coin.jpg", str).ConfigureAwait(false);
}
}
}

View File

@ -6,10 +6,8 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using NadekoBot.Services;
using Discord.WebSocket;
using NadekoBot.Services.Database.Models;
using System.Collections.Generic;
using NadekoBot.Services.Database;
namespace NadekoBot.Modules.Gambling
{
@ -19,8 +17,8 @@ namespace NadekoBot.Modules.Gambling
public static string CurrencyName { get; set; }
public static string CurrencyPluralName { get; set; }
public static string CurrencySign { get; set; }
public Gambling(ILocalization loc, CommandService cmds, ShardedDiscordClient client) : base(loc, cmds, client)
static Gambling()
{
using (var uow = DbHandler.UnitOfWork())
{
@ -42,156 +40,138 @@ namespace NadekoBot.Modules.Gambling
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Raffle(IUserMessage umsg, [Remainder] IRole role = null)
public async Task Raffle([Remainder] IRole role = null)
{
var channel = (ITextChannel)umsg.Channel;
role = role ?? channel.Guild.EveryoneRole;
role = role ?? Context.Guild.EveryoneRole;
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 Context.Channel.SendConfirmAsync("🎟 Raffled user", $"**{usr.Username}#{usr.Discriminator}** ID: `{usr.Id}`").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[Priority(0)]
public async Task Cash(IUserMessage umsg, [Remainder] IUser user = null)
public async Task Cash([Remainder] IUser user = null)
{
var channel = umsg.Channel;
user = user ?? Context.User;
user = user ?? umsg.Author;
await channel.SendMessageAsync($"{user.Username} has {GetCurrency(user.Id)} {CurrencySign}").ConfigureAwait(false);
await Context.Channel.SendConfirmAsync($"{user.Username} has {GetCurrency(user.Id)} {CurrencySign}").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[Priority(1)]
public async Task Cash(IUserMessage umsg, ulong userId)
public async Task Cash(ulong userId)
{
var channel = umsg.Channel;
await channel.SendMessageAsync($"`{userId}` has {GetCurrency(userId)} {CurrencySign}").ConfigureAwait(false);
await Context.Channel.SendConfirmAsync($"`{userId}` has {GetCurrency(userId)} {CurrencySign}").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Give(IUserMessage umsg, long amount, [Remainder] IGuildUser receiver)
public async Task Give(long amount, [Remainder] IGuildUser receiver)
{
var channel = (ITextChannel)umsg.Channel;
if (amount <= 0 || umsg.Author.Id == receiver.Id)
if (amount <= 0 || Context.User.Id == receiver.Id)
return;
var success = await CurrencyHandler.RemoveCurrencyAsync((IGuildUser)umsg.Author, $"Gift to {receiver.Username} ({receiver.Id}).", amount, true).ConfigureAwait(false);
var success = await CurrencyHandler.RemoveCurrencyAsync((IGuildUser)Context.User, $"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 Context.Channel.SendErrorAsync($"{Context.User.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 CurrencyHandler.AddCurrencyAsync(receiver, $"Gift from {Context.User.Username} ({Context.User.Id}).", amount, true).ConfigureAwait(false);
await Context.Channel.SendConfirmAsync($"{Context.User.Mention} successfully sent {amount} {(amount == 1 ? Gambling.CurrencyName : Gambling.CurrencyPluralName)} to {receiver.Mention}!").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[OwnerOnly]
[Priority(2)]
public Task Award(IUserMessage umsg, int amount, [Remainder] IGuildUser usr) =>
Award(umsg, amount, usr.Id);
public Task Award(int amount, [Remainder] IGuildUser usr) =>
Award(amount, usr.Id);
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[OwnerOnly]
[Priority(1)]
public async Task Award(IUserMessage umsg, int amount, ulong usrId)
public async Task Award(int amount, ulong usrId)
{
var channel = (ITextChannel)umsg.Channel;
if (amount <= 0)
return;
await CurrencyHandler.AddCurrencyAsync(usrId, $"Awarded by bot owner. ({umsg.Author.Username}/{umsg.Author.Id})", amount).ConfigureAwait(false);
await CurrencyHandler.AddCurrencyAsync(usrId, $"Awarded by bot owner. ({Context.User.Username}/{Context.User.Id})", amount).ConfigureAwait(false);
await channel.SendMessageAsync($"{umsg.Author.Mention} successfully awarded {amount} {(amount == 1 ? Gambling.CurrencyName : Gambling.CurrencyPluralName)} to <@{usrId}>!").ConfigureAwait(false);
await Context.Channel.SendConfirmAsync($"{Context.User.Mention} awarded {amount} {(amount == 1 ? Gambling.CurrencyName : Gambling.CurrencyPluralName)} to <@{usrId}>!").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[OwnerOnly]
[Priority(0)]
public async Task Award(IUserMessage umsg, int amount, [Remainder] IRole role)
public async Task Award(int amount, [Remainder] IRole role)
{
var channel = (ITextChannel)umsg.Channel;
var users = channel.Guild.GetUsers()
.Where(u => u.Roles.Contains(role))
var channel = (ITextChannel)Context.Channel;
var users = (await Context.Guild.GetUsersAsync())
.Where(u => u.GetRoles().Contains(role))
.ToList();
await Task.WhenAll(users.Select(u => CurrencyHandler.AddCurrencyAsync(u.Id,
$"Awarded by bot owner to **{role.Name}** role. ({umsg.Author.Username}/{umsg.Author.Id})",
$"Awarded by bot owner to **{role.Name}** role. ({Context.User.Username}/{Context.User.Id})",
amount)))
.ConfigureAwait(false);
await channel.SendMessageAsync($"Awarded `{amount}` {Gambling.CurrencyPluralName} to `{users.Count}` users from `{role.Name}` role.")
await Context.Channel.SendConfirmAsync($"Awarded `{amount}` {Gambling.CurrencyPluralName} to `{users.Count}` users from `{role.Name}` role.")
.ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[OwnerOnly]
public async Task Take(IUserMessage umsg, long amount, [Remainder] IGuildUser user)
{
var channel = (ITextChannel)umsg.Channel;
if (amount <= 0)
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);
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);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[OwnerOnly]
public async Task Take(IUserMessage umsg, long amount, [Remainder] ulong usrId)
public async Task Take(long amount, [Remainder] IGuildUser user)
{
var channel = (ITextChannel)umsg.Channel;
if (amount <= 0)
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);
if (await CurrencyHandler.RemoveCurrencyAsync(user, $"Taken by bot owner.({Context.User.Username}/{Context.User.Id})", amount, true).ConfigureAwait(false))
await Context.Channel.SendConfirmAsync($"{Context.User.Mention} successfully took {amount} {(amount == 1 ? 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 `{usrId}` because the user doesn't have that much {Gambling.CurrencyPluralName}!").ConfigureAwait(false);
await Context.Channel.SendErrorAsync($"{Context.User.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);
}
[NadekoCommand, Usage, Description, Aliases]
[OwnerOnly]
public async Task Take(long amount, [Remainder] ulong usrId)
{
if (amount <= 0)
return;
if (await CurrencyHandler.RemoveCurrencyAsync(usrId, $"Taken by bot owner.({Context.User.Username}/{Context.User.Id})", amount).ConfigureAwait(false))
await Context.Channel.SendConfirmAsync($"{Context.User.Mention} successfully took {amount} {(amount == 1 ? Gambling.CurrencyName : Gambling.CurrencyPluralName)} from <@{usrId}>!").ConfigureAwait(false);
else
await Context.Channel.SendErrorAsync($"{Context.User.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]
[RequireContext(ContextType.Guild)]
public async Task BetRoll(IUserMessage umsg, long amount)
public async Task BetRoll(long amount)
{
var channel = (ITextChannel)umsg.Channel;
if (amount < 1)
return;
var guildUser = (IGuildUser)umsg.Author;
long userFlowers;
using (var uow = DbHandler.UnitOfWork())
{
userFlowers = uow.Currency.GetOrCreate(umsg.Author.Id).Amount;
userFlowers = uow.Currency.GetOrCreate(Context.User.Id).Amount;
}
if (userFlowers < amount)
{
await channel.SendMessageAsync($"{guildUser.Mention} You don't have enough {Gambling.CurrencyPluralName}. You only have {userFlowers}{Gambling.CurrencySign}.").ConfigureAwait(false);
await Context.Channel.SendErrorAsync($"{Context.User.Mention} You don't have enough {Gambling.CurrencyPluralName}. You only have {userFlowers}{Gambling.CurrencySign}.").ConfigureAwait(false);
return;
}
await CurrencyHandler.RemoveCurrencyAsync(guildUser, "Betroll Gamble", amount, false).ConfigureAwait(false);
await CurrencyHandler.RemoveCurrencyAsync(Context.User, "Betroll Gamble", amount, false).ConfigureAwait(false);
var rng = new NadekoRandom().Next(0, 101);
var str = $"{guildUser.Mention} `You rolled {rng}.` ";
var str = $"{Context.User.Mention} `You rolled {rng}.` ";
if (rng < 67)
{
str += "Better luck next time.";
@ -199,43 +179,40 @@ namespace NadekoBot.Modules.Gambling
else if (rng < 91)
{
str += $"Congratulations! You won {amount * 2}{Gambling.CurrencySign} for rolling above 66";
await CurrencyHandler.AddCurrencyAsync(guildUser, "Betroll Gamble", amount * 2, false).ConfigureAwait(false);
await CurrencyHandler.AddCurrencyAsync(Context.User, "Betroll Gamble", amount * 2, false).ConfigureAwait(false);
}
else if (rng < 100)
{
str += $"Congratulations! You won {amount * 3}{Gambling.CurrencySign} for rolling above 90.";
await CurrencyHandler.AddCurrencyAsync(guildUser, "Betroll Gamble", amount * 3, false).ConfigureAwait(false);
await CurrencyHandler.AddCurrencyAsync(Context.User, "Betroll Gamble", amount * 3, false).ConfigureAwait(false);
}
else
{
str += $"👑 Congratulations! You won {amount * 10}{Gambling.CurrencySign} for rolling **100**. 👑";
await CurrencyHandler.AddCurrencyAsync(guildUser, "Betroll Gamble", amount * 10, false).ConfigureAwait(false);
await CurrencyHandler.AddCurrencyAsync(Context.User, "Betroll Gamble", amount * 10, false).ConfigureAwait(false);
}
await channel.SendMessageAsync(str).ConfigureAwait(false);
await Context.Channel.SendConfirmAsync(str).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Leaderboard(IUserMessage umsg)
public async Task Leaderboard()
{
var channel = (ITextChannel)umsg.Channel;
IEnumerable<Currency> richest;
IEnumerable<Currency> richest = new List<Currency>();
using (var uow = DbHandler.UnitOfWork())
{
richest = uow.Currency.GetTopRichest(10);
}
if (!richest.Any())
return;
await channel.SendMessageAsync(
await Context.Channel.SendMessageAsync(
richest.Aggregate(new StringBuilder(
$@"```xl
Id $$$
"),
(cur, cs) => cur.AppendLine($@"┣━━━━━━━━━━━━━━━━━━━━━╋━━━━━━━━┫
{(channel.Guild.GetUser(cs.UserId)?.Username.TrimTo(18, true) ?? cs.UserId.ToString()),-20} {cs.Amount,6} ")
{(Context.Guild.GetUserAsync(cs.UserId).GetAwaiter().GetResult()?.Username?.TrimTo(18, true) ?? cs.UserId.ToString()),-20} {cs.Amount,6} ")
).ToString() + "┗━━━━━━━━━━━━━━━━━━━━━┻━━━━━━━━┛```").ConfigureAwait(false);
}
}

View File

@ -0,0 +1,292 @@
using Discord;
using Discord.Commands;
using Discord.WebSocket;
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 : ModuleBase
{
//channelId, game
public static ConcurrentDictionary<ulong, AcrophobiaGame> AcrophobiaGames { get; } = new ConcurrentDictionary<ulong, AcrophobiaGame>();
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Acro(int time = 60)
{
var channel = (ITextChannel)Context.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).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())))
.ConfigureAwait(false);
return;
}
var submissionClosedEmbed = GetEmbed();
await channel.EmbedAsync(submissionClosedEmbed).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(SocketMessage arg)
{
try
{
var msg = arg as SocketUserMessage;
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()).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()).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).ConfigureAwait(false);
}
public void EnsureStopped()
{
NadekoBot.Client.MessageReceived -= PotentialAcro;
if (!source.IsCancellationRequested)
source.Cancel();
}
}
}
}

View File

@ -1,11 +1,14 @@
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Services;
using NLog;
using Services.CleverBotApi;
using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
@ -14,43 +17,48 @@ namespace NadekoBot.Modules.Games
public partial class Games
{
[Group]
public class CleverBotCommands
public class CleverBotCommands : ModuleBase
{
private static Logger _log { get; }
class CleverAnswer {
class CleverAnswer
{
public string Status { get; set; }
public string Response { get; set; }
}
//user#discrim is the key
public static ConcurrentHashSet<string> ChannelsInConversation { get; } = new ConcurrentHashSet<string>();
public static ConcurrentDictionary<ulong, ChatterBotSession> CleverbotGuilds { get; } = new ConcurrentDictionary<ulong, ChatterBotSession>();
public static ConcurrentDictionary<ulong, Lazy<ChatterBotSession>> CleverbotGuilds { get; } = new ConcurrentDictionary<ulong, Lazy<ChatterBotSession>>();
static CleverBotCommands()
{
_log = LogManager.GetCurrentClassLogger();
var sw = Stopwatch.StartNew();
using (var uow = DbHandler.UnitOfWork())
{
var bot = ChatterBotFactory.Create(ChatterBotType.CLEVERBOT);
CleverbotGuilds = new ConcurrentDictionary<ulong, ChatterBotSession>(
uow.GuildConfigs.GetAll()
CleverbotGuilds = new ConcurrentDictionary<ulong, Lazy<ChatterBotSession>>(
NadekoBot.AllGuildConfigs
.Where(gc => gc.CleverbotEnabled)
.ToDictionary(gc => gc.GuildId, gc => bot.CreateSession()));
.ToDictionary(gc => gc.GuildId, gc => new Lazy<ChatterBotSession>(() => bot.CreateSession(), true)));
}
sw.Stop();
_log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s");
}
public static async Task<bool> TryAsk(IUserMessage msg) {
public static async Task<bool> TryAsk(SocketUserMessage msg)
{
var channel = msg.Channel as ITextChannel;
if (channel == null)
return false;
ChatterBotSession cleverbot;
Lazy<ChatterBotSession> cleverbot;
if (!CleverbotGuilds.TryGetValue(channel.Guild.Id, out cleverbot))
return false;
var nadekoId = NadekoBot.Client.GetCurrentUser().Id;
var nadekoId = NadekoBot.Client.CurrentUser().Id;
var normalMention = $"<@{nadekoId}> ";
var nickMention = $"<@!{nadekoId}> ";
string message;
@ -69,51 +77,49 @@ namespace NadekoBot.Modules.Games
await msg.Channel.TriggerTypingAsync().ConfigureAwait(false);
var response = await cleverbot.Think(message).ConfigureAwait(false);
var response = await cleverbot.Value.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;
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[RequirePermission(ChannelPermission.ManageMessages)]
public async Task Cleverbot(IUserMessage imsg)
[RequireUserPermission(GuildPermission.ManageMessages)]
public async Task Cleverbot()
{
var channel = (ITextChannel)imsg.Channel;
var channel = (ITextChannel)Context.Channel;
ChatterBotSession throwaway;
Lazy<ChatterBotSession> throwaway;
if (CleverbotGuilds.TryRemove(channel.Guild.Id, out throwaway))
{
using (var uow = DbHandler.UnitOfWork())
{
uow.GuildConfigs.SetCleverbotEnabled(channel.Guild.Id, false);
uow.GuildConfigs.SetCleverbotEnabled(Context.Guild.Id, false);
await uow.CompleteAsync().ConfigureAwait(false);
}
await channel.SendMessageAsync($"{imsg.Author.Mention} `Disabled cleverbot on this server.`").ConfigureAwait(false);
await Context.Channel.SendConfirmAsync($"{Context.User.Mention} Disabled cleverbot on this server.").ConfigureAwait(false);
return;
}
var cleverbot = ChatterBotFactory.Create(ChatterBotType.CLEVERBOT);
var session = cleverbot.CreateSession();
CleverbotGuilds.TryAdd(channel.Guild.Id, session);
CleverbotGuilds.TryAdd(channel.Guild.Id, new Lazy<ChatterBotSession>(() => cleverbot.CreateSession(), true));
using (var uow = DbHandler.UnitOfWork())
{
uow.GuildConfigs.SetCleverbotEnabled(channel.Guild.Id, true);
uow.GuildConfigs.SetCleverbotEnabled(Context.Guild.Id, true);
await uow.CompleteAsync().ConfigureAwait(false);
}
await channel.SendMessageAsync($"{imsg.Author.Mention} `Enabled cleverbot on this server.`").ConfigureAwait(false);
await Context.Channel.SendConfirmAsync($"{Context.User.Mention} Enabled cleverbot on this server.").ConfigureAwait(false);
}
}
}
}
}

View File

@ -0,0 +1,200 @@
using Discord;
using Discord.WebSocket;
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.Threading.Tasks;
namespace NadekoBot.Modules.Games.Commands.Hangman
{
public class HangmanTermPool
{
const string termsPath = "data/hangman.json";
public static IReadOnlyDictionary<string, HangmanObject[]> data { get; }
static HangmanTermPool()
{
try
{
data = JsonConvert.DeserializeObject<Dictionary<string, HangmanObject[]>>(File.ReadAllText(termsPath));
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
public static HangmanObject GetTerm(string type)
{
if (string.IsNullOrWhiteSpace(type))
throw new ArgumentNullException(nameof(type));
type = type.Trim();
var rng = new NadekoRandom();
if (type == "All") {
var keys = data.Keys.ToArray();
type = keys[rng.Next(0, keys.Length)];
}
HangmanObject[] termTypes;
data.TryGetValue(type, out termTypes);
if (termTypes.Length == 0)
return null;
return termTypes[rng.Next(0, termTypes.Length)];
}
}
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 string TermType { get; }
public event Action<HangmanGame> OnEnded;
public HangmanGame(IMessageChannel channel, string type)
{
_log = LogManager.GetCurrentClassLogger();
this.GameChannel = channel;
this.TermType = type.ToTitleCase();
}
public void Start()
{
this.Term = HangmanTermPool.GetTerm(TermType);
if (this.Term == null)
throw new KeyNotFoundException("Can't find a term with that type. Use hangmanlist command.");
// 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))
.WithImageUrl(Term.ImageUrl)
.WithFooter(efb => efb.WithText(string.Join(" ", Guesses)));
if (Errors >= MaxErrors)
await GameChannel.EmbedAsync(embed.WithErrorColor()).ConfigureAwait(false);
else
await GameChannel.EmbedAsync(embed.WithOkColor()).ConfigureAwait(false);
}
private async void PotentialGuess(SocketMessage msg)
{
try
{
if (!(msg is SocketUserMessage))
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,8 @@
namespace NadekoBot.Modules.Games.Commands.Hangman
{
public class HangmanObject
{
public string Word { get; set; }
public string ImageUrl { get; set; }
}
}

View File

@ -0,0 +1,64 @@
using Discord.Commands;
using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Modules.Games.Commands.Hangman;
using NLog;
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
namespace NadekoBot.Modules.Games
{
public partial class Games
{
[Group]
public class HangmanCommands : ModuleBase
{
private static Logger _log { get; }
//channelId, game
public static ConcurrentDictionary<ulong, HangmanGame> HangmanGames { get; } = new ConcurrentDictionary<ulong, HangmanGame>();
private static string typesStr { get; } = "";
static HangmanCommands()
{
_log = LogManager.GetCurrentClassLogger();
typesStr = $"`List of \"{NadekoBot.ModulePrefixes[typeof(Games).Name]}hangman\" term types:`\n" + String.Join(", ", HangmanTermPool.data.Keys);
}
[NadekoCommand, Usage, Description, Aliases]
public async Task Hangmanlist()
{
await Context.Channel.SendConfirmAsync(typesStr);
}
[NadekoCommand, Usage, Description, Aliases]
public async Task Hangman([Remainder]string type = "All")
{
var hm = new HangmanGame(Context.Channel, type);
if (!HangmanGames.TryAdd(Context.Channel.Id, hm))
{
await Context.Channel.SendErrorAsync("Hangman game already running on this channel.").ConfigureAwait(false);
return;
}
hm.OnEnded += (g) =>
{
HangmanGame throwaway;
HangmanGames.TryRemove(g.GameChannel.Id, out throwaway);
};
try
{
hm.Start();
}
catch (Exception ex) {
try { await Context.Channel.SendErrorAsync($"Starting errored: {ex.Message}").ConfigureAwait(false); } catch { }
return;
}
await Context.Channel.SendConfirmAsync("Hangman game started", hm.ScrambledWord + "\n" + hm.GetHangman() + "\n" + hm.ScrambledWord);
}
}
}
}

View File

@ -13,15 +13,12 @@ namespace NadekoBot.Modules.Games
public partial class Games
{
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Leet(IUserMessage umsg, int level, [Remainder] string text = null)
public async Task Leet(int level, [Remainder] string text = null)
{
var channel = (ITextChannel)umsg.Channel;
text = text.Trim();
if (string.IsNullOrWhiteSpace(text))
return;
await channel.SendMessageAsync(ToLeet(text, level)).ConfigureAwait(false);
await Context.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;
@ -9,6 +10,7 @@ using NLog;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
@ -16,8 +18,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>
@ -28,53 +28,61 @@ namespace NadekoBot.Modules.Games
/// https://discord.gg/0TYNJfCU4De7YIk8
/// </summary>
[Group]
public class PlantPickCommands
public class PlantPickCommands : ModuleBase
{
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();
var sw = Stopwatch.StartNew();
#if !GLOBAL_NADEKO
NadekoBot.Client.MessageReceived += PotentialFlowerGeneration;
rng = new NadekoRandom();
#endif
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;
}
sw.Stop();
_log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s");
}
private Task PotentialFlowerGeneration(IMessage imsg)
private static async void PotentialFlowerGeneration(SocketMessage imsg)
{
var msg = imsg as IUserMessage;
if (msg == null || msg.IsAuthor() || msg.Author.IsBot)
return Task.CompletedTask;
var channel = imsg.Channel as ITextChannel;
if (channel == null)
return Task.CompletedTask;
if (!generationChannels.Contains(channel.Id))
return Task.CompletedTask;
var t = Task.Run(async () =>
try
{
var msg = imsg as SocketUserMessage;
if (msg == null || msg.IsAuthor() || msg.Author.IsBot)
return;
var channel = imsg.Channel as ITextChannel;
if (channel == null)
return;
if (!generationChannels.Contains(channel.Id))
return;
var lastGeneration = lastGenerations.GetOrAdd(channel.Id, DateTime.MinValue);
var rng = new NadekoRandom();
if (DateTime.Now - TimeSpan.FromSeconds(cooldown) < lastGeneration) //recently generated in this channel, don't generate again
return;
@ -84,59 +92,64 @@ 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 { }
var sent = await channel.SendFileAsync(
File.Open(GetRandomCurrencyImagePath(), FileMode.OpenOrCreate),
"RandomFlower.jpg",
$"❗ 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; });
}
});
return Task.CompletedTask;
}
catch { }
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Pick(IUserMessage imsg)
public async Task Pick()
{
var channel = (ITextChannel)imsg.Channel;
var channel = (ITextChannel)Context.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 (!(await channel.Guild.GetCurrentUserAsync()).GetPermissions(channel).ManageMessages)
return;
}
#if GLOBAL_NADEKO
if (!usersRecentlyPicked.Add(Context.User.Id))
return;
#endif
try
{
List<IUserMessage> msgs;
List<IUserMessage> msgs;
try { await imsg.DeleteAsync().ConfigureAwait(false); } catch { }
try { await Context.Message.DeleteAsync().ConfigureAwait(false); } catch { }
if (!plantedFlowers.TryRemove(channel.Id, out msgs))
return;
await Task.WhenAll(msgs.Select(toDelete => toDelete.DeleteAsync())).ConfigureAwait(false);
await Task.WhenAll(msgs.Select(toDelete => toDelete.DeleteAsync())).ConfigureAwait(false);
await CurrencyHandler.AddCurrencyAsync((IGuildUser)imsg.Author, "Picked flower(s).", msgs.Count, false).ConfigureAwait(false);
var msg = await channel.SendMessageAsync($"**{imsg.Author.Username}** picked {msgs.Count}{Gambling.Gambling.CurrencySign}!").ConfigureAwait(false);
var t = Task.Run(async () =>
await CurrencyHandler.AddCurrencyAsync((IGuildUser)Context.User, "Picked flower(s).", msgs.Count, false).ConfigureAwait(false);
var msg = await channel.SendConfirmAsync($"**{Context.User}** 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); }
});
#if GLOBAL_NADEKO
await Task.Delay(60000);
usersRecentlyPicked.TryRemove(Context.User.Id);
#endif
}
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Plant(IUserMessage imsg)
public async Task Plant()
{
var channel = (ITextChannel)imsg.Channel;
var removed = await CurrencyHandler.RemoveCurrencyAsync((IGuildUser)imsg.Author, "Planted a flower.", 1, false).ConfigureAwait(false);
var removed = await CurrencyHandler.RemoveCurrencyAsync((IGuildUser)Context.User, "Planted a flower.", 1, false).ConfigureAwait(false);
if (!removed)
{
await channel.SendMessageAsync($"You don't have any {Gambling.Gambling.CurrencyPluralName}.").ConfigureAwait(false);
await Context.Channel.SendErrorAsync($"You don't have any {Gambling.Gambling.CurrencyPluralName}.").ConfigureAwait(false);
return;
}
@ -144,29 +157,29 @@ namespace NadekoBot.Modules.Games
IUserMessage msg;
var vowelFirst = new[] { 'a', 'e', 'i', 'o', 'u' }.Contains(Gambling.Gambling.CurrencyName[0]);
var msgToSend = $"Oh how Nice! **{imsg.Author.Username}** planted {(vowelFirst ? "an" : "a")} {Gambling.Gambling.CurrencyName}. Pick it using {NadekoBot.ModulePrefixes[typeof(Games).Name]}pick";
var msgToSend = $"Oh how Nice! **{Context.User.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 Context.Channel.SendConfirmAsync(Gambling.Gambling.CurrencySign).ConfigureAwait(false);
}
else
{
msg = await channel.SendFileAsync(file, msgToSend).ConfigureAwait(false);
msg = await Context.Channel.SendFileAsync(File.Open(file, FileMode.OpenOrCreate), "plant.jpg", msgToSend).ConfigureAwait(false);
}
plantedFlowers.AddOrUpdate(channel.Id, new List<IUserMessage>() { msg }, (id, old) => { old.Add(msg); return old; });
plantedFlowers.AddOrUpdate(Context.Channel.Id, new List<IUserMessage>() { msg }, (id, old) => { old.Add(msg); return old; });
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[RequirePermission(GuildPermission.ManageMessages)]
public async Task GenCurrency(IUserMessage imsg)
[RequireUserPermission(GuildPermission.ManageMessages)]
public async Task GenCurrency()
{
var channel = (ITextChannel)imsg.Channel;
var channel = (ITextChannel)Context.Channel;
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 +198,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,10 +1,13 @@
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using NadekoBot.Attributes;
using NadekoBot.Extensions;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
@ -12,137 +15,162 @@ namespace NadekoBot.Modules.Games
{
public partial class Games
{
public static ConcurrentDictionary<IGuild, Poll> ActivePolls = new ConcurrentDictionary<IGuild, Poll>();
[NadekoCommand, Usage, Description, Aliases]
[RequirePermission(GuildPermission.ManageMessages)]
[RequireContext(ContextType.Guild)]
public Task Poll(IUserMessage umsg, [Remainder] string arg = null)
=> InternalStartPoll(umsg, arg, isPublic: false);
[NadekoCommand, Usage, Description, Aliases]
[RequirePermission(GuildPermission.ManageMessages)]
[RequireContext(ContextType.Guild)]
public Task PublicPoll(IUserMessage umsg, [Remainder] string arg = null)
=> InternalStartPoll(umsg, arg, isPublic: true);
private async Task InternalStartPoll(IUserMessage umsg, string arg, bool isPublic = false)
[Group]
public class PollCommands : ModuleBase
{
var channel = (ITextChannel)umsg.Channel;
public static ConcurrentDictionary<ulong, Poll> ActivePolls = new ConcurrentDictionary<ulong, Poll>();
if (!(umsg.Author as IGuildUser).GuildPermissions.ManageChannels)
return;
if (string.IsNullOrWhiteSpace(arg) || !arg.Contains(";"))
return;
var data = arg.Split(';');
if (data.Length < 3)
return;
[NadekoCommand, Usage, Description, Aliases]
[RequireUserPermission(GuildPermission.ManageMessages)]
[RequireContext(ContextType.Guild)]
public Task Poll([Remainder] string arg = null)
=> InternalStartPoll(arg, isPublic: false);
var poll = new Poll(umsg, data[0], data.Skip(1), isPublic: isPublic);
if (ActivePolls.TryAdd(channel.Guild, poll))
[NadekoCommand, Usage, Description, Aliases]
[RequireUserPermission(GuildPermission.ManageMessages)]
[RequireContext(ContextType.Guild)]
public Task PublicPoll([Remainder] string arg = null)
=> InternalStartPoll(arg, isPublic: true);
[NadekoCommand, Usage, Description, Aliases]
[RequireUserPermission(GuildPermission.ManageMessages)]
[RequireContext(ContextType.Guild)]
public async Task PollStats()
{
await poll.StartPoll().ConfigureAwait(false);
Games.Poll poll;
if (!ActivePolls.TryGetValue(Context.Guild.Id, out poll))
return;
await Context.Channel.EmbedAsync(poll.GetStats("Current Poll Results"));
}
private async Task InternalStartPoll(string arg, bool isPublic = false)
{
var channel = (ITextChannel)Context.Channel;
if (!(Context.User as IGuildUser).GuildPermissions.ManageChannels)
return;
if (string.IsNullOrWhiteSpace(arg) || !arg.Contains(";"))
return;
var data = arg.Split(';');
if (data.Length < 3)
return;
var poll = new Poll(Context.Message, data[0], data.Skip(1), isPublic: isPublic);
if (ActivePolls.TryAdd(channel.Guild.Id, poll))
{
await poll.StartPoll().ConfigureAwait(false);
}
else
await channel.SendErrorAsync("Poll is already running on this server.").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireUserPermission(GuildPermission.ManageMessages)]
[RequireContext(ContextType.Guild)]
public async Task Pollend()
{
var channel = (ITextChannel)Context.Channel;
Poll poll;
ActivePolls.TryRemove(channel.Guild.Id, out poll);
await poll.StopPoll().ConfigureAwait(false);
}
else
await channel.SendMessageAsync("`Poll is already running on this server.`").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequirePermission(GuildPermission.ManageMessages)]
[RequireContext(ContextType.Guild)]
public async Task Pollend(IUserMessage umsg)
public class Poll
{
var channel = (ITextChannel)umsg.Channel;
private readonly IUserMessage originalMessage;
private readonly IGuild guild;
private string[] Answers { get; }
private ConcurrentDictionary<ulong, int> participants = new ConcurrentDictionary<ulong, int>();
private readonly string question;
private DateTime started;
private CancellationTokenSource pollCancellationSource = new CancellationTokenSource();
public bool IsPublic { get; }
Poll poll;
ActivePolls.TryRemove(channel.Guild, out poll);
await poll.StopPoll().ConfigureAwait(false);
}
}
public Poll(IUserMessage umsg, string question, IEnumerable<string> enumerable, bool isPublic = false)
{
this.originalMessage = umsg;
this.guild = ((ITextChannel)umsg.Channel).Guild;
this.question = question;
this.Answers = enumerable as string[] ?? enumerable.ToArray();
this.IsPublic = isPublic;
}
public class Poll
{
private readonly IUserMessage originalMessage;
private readonly IGuild guild;
private readonly string[] answers;
private ConcurrentDictionary<ulong, int> participants = new ConcurrentDictionary<ulong, int>();
private readonly string question;
private DateTime started;
private CancellationTokenSource pollCancellationSource = new CancellationTokenSource();
private readonly bool isPublic;
public Poll(IUserMessage umsg, string question, IEnumerable<string> enumerable, bool isPublic = false)
{
this.originalMessage = umsg;
this.guild = ((ITextChannel)umsg.Channel).Guild;
this.question = question;
this.answers = enumerable as string[] ?? enumerable.ToArray();
this.isPublic = isPublic;
}
public async Task StartPoll()
{
started = DateTime.Now;
NadekoBot.Client.MessageReceived += Vote;
var msgToSend = $"📃**{originalMessage.Author.Username}** has created a poll which requires your attention:\n\n**{question}**\n";
var num = 1;
msgToSend = answers.Aggregate(msgToSend, (current, answ) => current + $"`{num++}.` **{answ}**\n");
if (!isPublic)
msgToSend += "\n**Private Message me with the corresponding number of the answer.**";
else
msgToSend += "\n**Send a Message here with the corresponding number of the answer.**";
await originalMessage.Channel.SendMessageAsync(msgToSend).ConfigureAwait(false);
}
public async Task StopPoll()
{
NadekoBot.Client.MessageReceived -= Vote;
try
public EmbedBuilder GetStats(string title)
{
var results = participants.GroupBy(kvp => kvp.Value)
.ToDictionary(x => x.Key, x => x.Sum(kvp => 1))
.OrderByDescending(kvp => kvp.Value);
.ToDictionary(x => x.Key, x => x.Sum(kvp => 1))
.OrderByDescending(kvp => kvp.Value)
.ToArray();
var totalVotesCast = results.Sum(kvp => kvp.Value);
if (totalVotesCast == 0)
var eb = new EmbedBuilder().WithTitle(title);
var sb = new StringBuilder()
.AppendLine(Format.Bold(question))
.AppendLine();
var totalVotesCast = 0;
if (results.Length == 0)
{
await originalMessage.Channel.SendMessageAsync("📄 **No votes have been cast.**").ConfigureAwait(false);
return;
sb.AppendLine("No votes cast.");
}
else
{
for (int i = 0; i < results.Length; i++)
{
var result = results[i];
sb.AppendLine($"`{i + 1}.` {Format.Bold(Answers[result.Key - 1])} with {Format.Bold(result.Value.ToString())} votes.");
totalVotesCast += result.Value;
}
}
var closeMessage = $"--------------**POLL CLOSED**--------------\n" +
$"📄 , here are the results:\n";
closeMessage = results.Aggregate(closeMessage, (current, kvp) => current + $"`{kvp.Key}.` **[{answers[kvp.Key - 1]}]**" +
$" has {kvp.Value} votes." +
$"({kvp.Value * 1.0f / totalVotesCast * 100}%)\n");
await originalMessage.Channel.SendMessageAsync($"📄 **Total votes cast**: {totalVotesCast}\n{closeMessage}").ConfigureAwait(false);
eb.WithDescription(sb.ToString())
.WithFooter(efb => efb.WithText(totalVotesCast + " total votes cast."));
return eb;
}
catch (Exception ex)
public async Task StartPoll()
{
Console.WriteLine($"Error in poll game {ex}");
started = DateTime.Now;
NadekoBot.Client.MessageReceived += Vote;
var msgToSend = $"📃**{originalMessage.Author.Username}** has created a poll which requires your attention:\n\n**{question}**\n";
var num = 1;
msgToSend = Answers.Aggregate(msgToSend, (current, answ) => current + $"`{num++}.` **{answ}**\n");
if (!IsPublic)
msgToSend += "\n**Private Message me with the corresponding number of the answer.**";
else
msgToSend += "\n**Send a Message here with the corresponding number of the answer.**";
await originalMessage.Channel.SendConfirmAsync(msgToSend).ConfigureAwait(false);
}
}
private Task Vote(IMessage imsg)
{
// has to be a user message
var msg = imsg as IUserMessage;
if (msg == null || msg.Author.IsBot)
return Task.CompletedTask;
public async Task StopPoll()
{
NadekoBot.Client.MessageReceived -= Vote;
await originalMessage.Channel.EmbedAsync(GetStats("POLL CLOSED")).ConfigureAwait(false);
}
// has to be an integer
int vote;
if (!int.TryParse(imsg.Content, out vote))
return Task.CompletedTask;
if (vote < 1 || vote > answers.Length)
return Task.CompletedTask;
var t = Task.Run(async () =>
private async void Vote(SocketMessage imsg)
{
try
{
// has to be a user message
var msg = imsg as SocketUserMessage;
if (msg == null || msg.Author.IsBot)
return;
// has to be an integer
int vote;
if (!int.TryParse(imsg.Content, out vote))
return;
if (vote < 1 || vote > Answers.Length)
return;
IMessageChannel ch;
if (isPublic)
if (IsPublic)
{
//if public, channel must be the same the poll started in
if (originalMessage.Channel.Id != imsg.Channel.Id)
@ -164,21 +192,19 @@ namespace NadekoBot.Modules.Games
//user can vote only once
if (participants.TryAdd(msg.Author.Id, vote))
{
if (!isPublic)
if (!IsPublic)
{
await ch.SendMessageAsync($"Thanks for voting **{msg.Author.Username}**.").ConfigureAwait(false);
await ch.SendConfirmAsync($"Thanks for voting **{msg.Author.Username}**.").ConfigureAwait(false);
}
else
{
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

@ -1,5 +1,6 @@
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Modules.Games.Commands.Models;
@ -21,9 +22,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 +32,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 +46,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 +58,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,36 +106,39 @@ namespace NadekoBot.Modules.Games
NadekoBot.Client.MessageReceived += AnswerReceived;
}
private Task AnswerReceived(IMessage imsg)
private async void AnswerReceived(SocketMessage imsg)
{
if (imsg.Author.IsBot)
return Task.CompletedTask;
var msg = imsg as IUserMessage;
if (msg == null)
return Task.CompletedTask;
var t = Task.Run(async () =>
try
{
try
if (imsg.Author.IsBot)
return;
var msg = imsg as SocketUserMessage;
if (msg == null)
return;
if (this.Channel == null || this.Channel.Id != this.Channel.Id) return;
var guess = msg.Content;
var distance = CurrentSentence.LevenshteinDistance(guess);
var decision = Judge(distance, guess.Length);
if (decision && !finishedUserIds.Contains(msg.Author.Id))
{
if (channel == null || channel.Id != channel.Id) return;
var guess = msg.Content;
var distance = CurrentSentence.LevenshteinDistance(guess);
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 this.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
.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)))
.ConfigureAwait(false);
if (finishedUserIds.Count % 4 == 0)
{
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);
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 this.Channel.SendConfirmAsync($":exclamation: A lot of people finished, here is the text for those still typing:\n\n**{Format.Sanitize(CurrentSentence.Replace(" ", " \x200B")).SanitizeMentions()}**").ConfigureAwait(false);
}
}
catch { }
});
return Task.CompletedTask;
}
catch (Exception ex) { _log.Warn(ex); }
}
private bool Judge(int errors, int textLength) => errors <= textLength / 25;
@ -144,9 +146,8 @@ namespace NadekoBot.Modules.Games
}
[Group]
public class SpeedTypingCommands
public class SpeedTypingCommands : ModuleBase
{
public static List<TypingArticle> TypingArticles { get; } = new List<TypingArticle>();
const string typingArticlesPath = "data/typing_articles.json";
@ -155,24 +156,19 @@ namespace NadekoBot.Modules.Games
{
try { TypingArticles = JsonConvert.DeserializeObject<List<TypingArticle>>(File.ReadAllText(typingArticlesPath)); } catch { }
}
public static ConcurrentDictionary<ulong, TypingGame> RunningContests;
public SpeedTypingCommands()
{
RunningContests = new ConcurrentDictionary<ulong, TypingGame>();
}
public static ConcurrentDictionary<ulong, TypingGame> RunningContests = new ConcurrentDictionary<ulong, TypingGame>();
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task TypeStart(IUserMessage msg)
public async Task TypeStart()
{
var channel = (ITextChannel)msg.Channel;
var channel = (ITextChannel)Context.Channel;
var game = RunningContests.GetOrAdd(channel.Guild.Id, id => new TypingGame(channel));
if (game.IsActive)
{
await channel.SendMessageAsync(
await channel.SendErrorAsync(
$"Contest already running in " +
$"{game.Channel.Mention} channel.")
.ConfigureAwait(false);
@ -185,42 +181,42 @@ namespace NadekoBot.Modules.Games
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task TypeStop(IUserMessage imsg)
public async Task TypeStop()
{
var channel = (ITextChannel)imsg.Channel;
var channel = (ITextChannel)Context.Channel;
TypingGame game;
if (RunningContests.TryRemove(channel.Guild.Id, out game))
{
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);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[OwnerOnly]
public async Task Typeadd(IUserMessage imsg, [Remainder] string text)
public async Task Typeadd([Remainder] string text)
{
var channel = (ITextChannel)imsg.Channel;
var channel = (ITextChannel)Context.Channel;
TypingArticles.Add(new TypingArticle
{
Title = $"Text added on {DateTime.UtcNow} by {imsg.Author}",
Title = $"Text added on {DateTime.UtcNow} by {Context.User}",
Text = text.SanitizeMentions(),
});
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]
[RequireContext(ContextType.Guild)]
public async Task Typelist(IUserMessage imsg, int page = 1)
public async Task Typelist(int page = 1)
{
var channel = (ITextChannel)imsg.Channel;
var channel = (ITextChannel)Context.Channel;
if (page < 1)
return;
@ -229,20 +225,20 @@ namespace NadekoBot.Modules.Games
if (!articles.Any())
{
await channel.SendMessageAsync($"{imsg.Author.Mention} `No articles found on that page.`").ConfigureAwait(false);
await channel.SendErrorAsync($"{Context.User.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);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[OwnerOnly]
public async Task Typedel(IUserMessage imsg, int index)
public async Task Typedel(int index)
{
var channel = (ITextChannel)imsg.Channel;
var channel = (ITextChannel)Context.Channel;
index -= 1;
if (index < 0 || index >= TypingArticles.Count)
@ -253,7 +249,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

@ -1,5 +1,6 @@
using Discord;
using Discord.Net;
using Discord.WebSocket;
using NadekoBot.Extensions;
using NLog;
using System;
@ -22,7 +23,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,137 +36,162 @@ 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); }
catch (HttpException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound || ex.StatusCode == System.Net.HttpStatusCode.Forbidden)
{
break;
}
catch (Exception ex) { _log.Warn(ex); }
//receive messages
NadekoBot.Client.MessageReceived += PotentialGuess;
//allow people to guess
GameActive = true;
EmbedBuilder questionEmbed;
IUserMessage questionMessage;
try
{
//hint
await Task.Delay(HintTimeoutMiliseconds, token).ConfigureAwait(false);
if (ShowHints)
try { await channel.SendMessageAsync($":exclamation:**Hint:** {CurrentQuestion.GetHint()}").ConfigureAwait(false); }
catch (HttpException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound || ex.StatusCode == System.Net.HttpStatusCode.Forbidden)
{
break;
}
catch (Exception ex) { _log.Warn(ex); }
//timeout
await Task.Delay(QuestionDurationMiliseconds - HintTimeoutMiliseconds, token).ConfigureAwait(false);
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).ConfigureAwait(false);
}
catch (HttpException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound || ex.StatusCode == System.Net.HttpStatusCode.Forbidden)
{
return;
}
catch (Exception ex)
{
_log.Warn(ex);
await Task.Delay(2000).ConfigureAwait(false);
continue;
}
//receive messages
try
{
NadekoBot.Client.MessageReceived += PotentialGuess;
//allow people to guess
GameActive = true;
try
{
//hint
await Task.Delay(HintTimeoutMiliseconds, triviaCancelSource.Token).ConfigureAwait(false);
if (ShowHints)
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;
}
catch (Exception ex) { _log.Warn(ex); }
//timeout
await Task.Delay(QuestionDurationMiliseconds - HintTimeoutMiliseconds, triviaCancelSource.Token).ConfigureAwait(false);
}
catch (TaskCanceledException) { } //means someone guessed the answer
}
finally
{
GameActive = false;
NadekoBot.Client.MessageReceived -= PotentialGuess;
}
catch (TaskCanceledException) { } //means someone guessed the answer
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
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())).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)
private async void PotentialGuess(SocketMessage imsg)
{
if (imsg.Author.IsBot)
return Task.CompletedTask;
var umsg = imsg as IUserMessage;
if (umsg == null)
return Task.CompletedTask;
var t = Task.Run(async () =>
try
{
if (imsg.Author.IsBot)
return;
var umsg = imsg as SocketUserMessage;
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);
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;
var guildUser = umsg.Author as IGuildUser;
var guess = false;
await _guessLock.WaitAsync().ConfigureAwait(false);
try
if (GameActive && CurrentQuestion.IsAnswerCorrect(umsg.Content) && !triviaCancelSource.IsCancellationRequested)
{
if (GameActive && CurrentQuestion.IsAnswerCorrect(umsg.Content) && !triviaCancelSource.IsCancellationRequested)
{
Users.AddOrUpdate(guildUser, 1, (gu, old) => ++old);
guess = true;
}
Users.AddOrUpdate(guildUser, 1, (gu, old) => ++old);
guess = true;
}
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;
ShouldStopGame = true;
await channel.SendMessageAsync($":exclamation: We have a winner! It's {guildUser.Mention}.").ConfigureAwait(false);
}
catch (Exception ex) { _log.Warn(ex); }
});
return Task.CompletedTask;
finally { _guessLock.Release(); }
if (!guess) return;
triviaCancelSource.Cancel();
if (Users[guildUser] == WinRequirement)
{
ShouldStopGame = true;
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); }
}
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))
{
@ -175,4 +201,4 @@ namespace NadekoBot.Modules.Games.Trivia
return sb.ToString();
}
}
}
}

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,75 +1,84 @@
using Discord;
using Discord.Commands;
using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Modules.Games.Trivia;
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading.Tasks;
//todo Rewrite? Fix trivia not stopping bug
namespace NadekoBot.Modules.Games
{
public partial class Games
{
[Group]
public class TriviaCommands
public class TriviaCommands : ModuleBase
{
public static ConcurrentDictionary<ulong, TriviaGame> RunningTrivias = new ConcurrentDictionary<ulong, TriviaGame>();
public static ConcurrentDictionary<ulong, TriviaGame> RunningTrivias { get; } = new ConcurrentDictionary<ulong, TriviaGame>();
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Trivia(IUserMessage umsg, params string[] args)
{
var channel = (ITextChannel)umsg.Channel;
public Task Trivia([Remainder] string additionalArgs = "")
=> Trivia(10, additionalArgs);
TriviaGame trivia;
if (!RunningTrivias.TryGetValue(channel.Guild.Id, out trivia))
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Trivia(int winReq = 10, [Remainder] string additionalArgs = "")
{
var channel = (ITextChannel)Context.Channel;
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);
}
finally
{
RunningTrivias.TryRemove(channel.Guild.Id, out trivia);
await trivia.EnsureStopped().ConfigureAwait(false);
}
return;
}
else
await channel.SendMessageAsync("Trivia game is already running on this server.\n" + trivia.CurrentQuestion).ConfigureAwait(false);
await Context.Channel.SendErrorAsync("Trivia game is already running on this server.\n" + trivia.CurrentQuestion).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Tl(IUserMessage umsg)
public async Task Tl()
{
var channel = (ITextChannel)umsg.Channel;
var channel = (ITextChannel)Context.Channel;
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]
[RequireContext(ContextType.Guild)]
public async Task Tq(IUserMessage umsg)
public async Task Tq()
{
var channel = (ITextChannel)umsg.Channel;
var channel = (ITextChannel)Context.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,43 +21,35 @@ namespace NadekoBot.Modules.Games
}
}
}
public Games(ILocalization loc, CommandService cmds, ShardedDiscordClient client) : base(loc, cmds, client)
{
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Choose(IUserMessage umsg, [Remainder] string list = null)
public async Task Choose([Remainder] string list = null)
{
var channel = (ITextChannel)umsg.Channel;
if (string.IsNullOrWhiteSpace(list))
return;
var listArr = list.Split(';');
if (listArr.Count() < 2)
return;
var rng = new NadekoRandom();
await channel.SendMessageAsync(listArr[rng.Next(0, listArr.Length)]).ConfigureAwait(false);
await Context.Channel.SendConfirmAsync("🤔", listArr[rng.Next(0, listArr.Length)]).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task _8Ball(IUserMessage umsg, [Remainder] string question = null)
public async Task _8Ball([Remainder] string question = null)
{
var channel = (ITextChannel)umsg.Channel;
if (string.IsNullOrWhiteSpace(question))
return;
var rng = new NadekoRandom();
await channel.SendMessageAsync($@"❓ `Question` __**{question}**__
🎱 `8Ball Answers` __**{_8BallResponses.Shuffle().FirstOrDefault()}**__").ConfigureAwait(false);
await Context.Channel.EmbedAsync(new EmbedBuilder().WithColor(NadekoBot.OkColor)
.AddField(efb => efb.WithName("❓ Question").WithValue(question).WithIsInline(false))
.AddField(efb => efb.WithName("🎱 8Ball").WithValue(_8BallResponses.Shuffle().FirstOrDefault()).WithIsInline(false)));
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Rps(IUserMessage umsg, string input)
public async Task Rps(string input)
{
var channel = (ITextChannel)umsg.Channel;
Func<int,string> GetRPSPick = (p) =>
{
if (p == 0)
@ -95,20 +87,17 @@ namespace NadekoBot.Modules.Games
else if ((pick == 0 && nadekoPick == 1) ||
(pick == 1 && nadekoPick == 2) ||
(pick == 2 && nadekoPick == 0))
msg = $"{NadekoBot.Client.GetCurrentUser().Mention} won! {GetRPSPick(nadekoPick)} beats {GetRPSPick(pick)}";
msg = $"{NadekoBot.Client.CurrentUser().Mention} won! {GetRPSPick(nadekoPick)} beats {GetRPSPick(pick)}";
else
msg = $"{umsg.Author.Mention} won! {GetRPSPick(pick)} beats {GetRPSPick(nadekoPick)}";
msg = $"{Context.User.Mention} won! {GetRPSPick(pick)} beats {GetRPSPick(nadekoPick)}";
await channel.SendMessageAsync(msg).ConfigureAwait(false);
await Context.Channel.SendConfirmAsync(msg).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Linux(IUserMessage umsg, string guhnoo, string loonix)
public async Task Linux(string guhnoo, string loonix)
{
var channel = (ITextChannel)umsg.Channel;
await channel.SendMessageAsync(
await Context.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

@ -22,6 +22,8 @@ namespace NadekoBot.Modules.Help
static Help()
{
//todo don't cache this, just query db when someone wants -h
using (var uow = DbHandler.UnitOfWork())
{
var config = uow.BotConfig.GetOrCreate();
@ -30,105 +32,113 @@ namespace NadekoBot.Modules.Help
}
}
public Help(ILocalization loc, CommandService cmds, ShardedDiscordClient client) : base(loc, cmds, client)
public Help() : base()
{
}
[NadekoCommand, Usage, Description, Aliases]
public async Task Modules(IUserMessage umsg)
public async Task Modules()
{
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.GroupBy(m => m.GetTopLevelModule()).Select(m => m.Key.Name).OrderBy(s => s)));
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
public async Task Commands(IUserMessage umsg, [Remainder] string module = null)
public async Task Commands([Remainder] string module = null)
{
var channel = umsg.Channel;
var channel = Context.Channel;
module = module?.Trim().ToUpperInvariant();
if (string.IsNullOrWhiteSpace(module))
return;
var cmds = _commands.Commands.Where(c => c.Module.Name.ToUpperInvariant().StartsWith(module))
.OrderBy(c => c.Text)
var cmds = NadekoBot.CommandService.Commands.Where(c => c.Module.GetTopLevelModule().Name.ToUpperInvariant().StartsWith(module))
.OrderBy(c => c.Aliases.First())
.Distinct(new CommandTextEqualityComparer())
.AsEnumerable();
var cmdsArray = cmds as Command[] ?? cmds.ToArray();
var cmdsArray = cmds as CommandInfo[] ?? 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")
{
await channel.SendTableAsync("📃 **List Of Commands:**\n", cmdsArray, el => $"{el.Text,-15} {"["+el.Aliases.Skip(1).FirstOrDefault()+"]",-8}").ConfigureAwait(false);
await channel.SendTableAsync("📃 **List Of Commands:**\n", cmdsArray, el => $"{el.Aliases.First(),-15} {"["+el.Aliases.Skip(1).FirstOrDefault()+"]",-8}").ConfigureAwait(false);
}
else
{
await channel.SendMessageAsync("📃 **List Of Commands:**\n• " + string.Join("\n• ", cmdsArray.Select(c => $"{c.Text}")));
await channel.SendMessageAsync("📃 **List Of Commands:**\n• " + string.Join("\n• ", cmdsArray.Select(c => $"{c.Aliases.First()}")));
}
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]
public async Task H(IUserMessage umsg, [Remainder] string comToFind = null)
public async Task H([Remainder] string comToFind = null)
{
var channel = umsg.Channel;
var channel = Context.Channel;
comToFind = comToFind?.ToLowerInvariant();
if (string.IsNullOrWhiteSpace(comToFind))
{
IMessageChannel ch = channel is ITextChannel ? await ((IGuildUser)umsg.Author).CreateDMChannelAsync() : channel;
IMessageChannel ch = channel is ITextChannel ? await ((IGuildUser)Context.User).CreateDMChannelAsync() : channel;
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.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.Aliases.First()}`**";
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.WithName(str).WithValue($"{ string.Format(com.Summary, com.Module.Aliases.First())} { GetCommandRequirements(com)}").WithIsInline(true))
.AddField(fb => fb.WithName("**Usage**").WithValue($"{string.Format(com.Remarks, com.Module.Aliases.First())}").WithIsInline(false))
.WithColor(NadekoBot.OkColor);
await channel.EmbedAsync(embed).ConfigureAwait(false);
}
private string GetCommandRequirements(Command cmd)
{
return String.Join(" ", cmd.Source.CustomAttributes
.Where(ca => ca.AttributeType == typeof(OwnerOnlyAttribute) || ca.AttributeType == typeof(RequirePermissionAttribute))
.Select(ca =>
{
if (ca.AttributeType == typeof(OwnerOnlyAttribute))
return "**Bot Owner only.**";
else if (ca.AttributeType == typeof(RequirePermissionAttribute))
return $"**Requires {(GuildPermission)ca.ConstructorArguments.FirstOrDefault().Value} server permission.**".Replace("Guild", "Server");
else
return $"**Requires {(GuildPermission)ca.ConstructorArguments.FirstOrDefault().Value} channel permission.**".Replace("Guild", "Server");
}));
}
private string GetCommandRequirements(CommandInfo cmd) =>
String.Join(" ", cmd.Preconditions
.Where(ca => ca is OwnerOnlyAttribute || ca is RequireUserPermissionAttribute)
.Select(ca =>
{
if (ca is OwnerOnlyAttribute)
return "**Bot Owner only.**";
var cau = (RequireUserPermissionAttribute)ca;
if (cau.GuildPermission != null)
return $"**Requires {cau.GuildPermission} server permission.**".Replace("Guild", "Server");
else
return $"**Requires {cau.ChannelPermission} channel permission.**".Replace("Guild", "Server");
}));
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[OwnerOnly]
public Task Hgit(IUserMessage umsg)
public async Task Hgit()
{
var helpstr = new StringBuilder();
helpstr.AppendLine("You can support the project on patreon: <https://patreon.com/nadekobot> or paypal: <https://www.paypal.me/Kwoth>\n");
helpstr.AppendLine("##Table Of Contents");
helpstr.AppendLine(string.Join("\n", NadekoBot.CommandService.Modules.Where(m => m.Name.ToLowerInvariant() != "help").OrderBy(m => m.Name).Prepend(NadekoBot.CommandService.Modules.FirstOrDefault(m=>m.Name.ToLowerInvariant()=="help")).Select(m => $"- [{m.Name}](#{m.Name.ToLowerInvariant()})")));
helpstr.AppendLine(string.Join("\n", NadekoBot.CommandService.Modules.Where(m => m.GetTopLevelModule().Name.ToLowerInvariant() != "help")
.Select(m => m.GetTopLevelModule().Name)
.Distinct()
.OrderBy(m => m)
.Prepend("Help")
.Select(m => $"- [{m}](#{m.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.GetTopLevelModule().Name).GroupBy(c => c.Aliases.First()).Select(g => g.First()))
{
if (com.Module.Name != lastModule)
var module = com.Module.GetTopLevelModule();
if (module.Name != lastModule)
{
if (lastModule != null)
{
@ -136,36 +146,36 @@ namespace NadekoBot.Modules.Help
helpstr.AppendLine("###### [Back to TOC](#table-of-contents)");
}
helpstr.AppendLine();
helpstr.AppendLine("### " + com.Module.Name + " ");
helpstr.AppendLine("### " + module.Name + " ");
helpstr.AppendLine("Command and aliases | Description | Usage");
helpstr.AppendLine("----------------|--------------|-------");
lastModule = com.Module.Name;
lastModule = module.Name;
}
helpstr.AppendLine($"`{com.Text}` {string.Join(" ", com.Aliases.Skip(1).Select(a=>"`"+a+"`"))} | {string.Format(com.Summary, com.Module.Prefix)} {GetCommandRequirements(com)} | {string.Format(com.Remarks, com.Module.Prefix)}");
helpstr.AppendLine($"{string.Join(" ", com.Aliases.Select(a => "`" + a + "`"))} | {string.Format(com.Summary, com.Module.GetPrefix())} {GetCommandRequirements(com)} | {string.Format(com.Remarks, com.Module.GetPrefix())}");
}
helpstr = helpstr.Replace(NadekoBot.Client.GetCurrentUser().Username , "@BotName");
helpstr = helpstr.Replace(NadekoBot.Client.CurrentUser().Username , "@BotName");
File.WriteAllText("../../docs/Commands List.md", helpstr.ToString());
return Task.CompletedTask;
await Context.Channel.SendConfirmAsync("Commandlist Regenerated").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Guide(IUserMessage umsg)
public async Task Guide()
{
var channel = (ITextChannel)umsg.Channel;
var channel = (ITextChannel)Context.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);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Donate(IUserMessage umsg)
public async Task Donate()
{
var channel = (ITextChannel)umsg.Channel;
var channel = (ITextChannel)Context.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.
@ -174,11 +184,11 @@ Don't forget to leave your discord name or id in the message.
}
}
public class CommandTextEqualityComparer : IEqualityComparer<Command>
public class CommandTextEqualityComparer : IEqualityComparer<CommandInfo>
{
public bool Equals(Command x, Command y) => x.Text == y.Text;
public bool Equals(CommandInfo x, CommandInfo y) => x.Aliases.First() == y.Aliases.First();
public int GetHashCode(Command obj) => obj.Text.GetHashCode();
public int GetHashCode(CommandInfo obj) => obj.Aliases.First().GetHashCode();
}
}

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,11 @@ 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 event Action<Song, int> SongRemoved = delegate { };
public MusicPlayer(IVoiceChannel startingVoiceChannel, float? defaultVolume)
{
@ -106,11 +131,13 @@ namespace NadekoBot.Modules.Music.Classes
}
CurrentSong = GetNextSong();
RemoveSongAt(0);
if (CurrentSong == null)
continue;
var index = playlist.IndexOf(CurrentSong);
if (index != -1)
RemoveSongAt(index, true);
OnStarted(this, CurrentSong);
await CurrentSong.Play(audioClient, cancelToken);
@ -129,6 +156,7 @@ namespace NadekoBot.Modules.Music.Classes
{
Console.WriteLine("Music thread almost crashed.");
Console.WriteLine(ex);
await Task.Delay(3000).ConfigureAwait(false);
}
finally
{
@ -168,7 +196,7 @@ namespace NadekoBot.Modules.Music.Classes
});
}
public void TogglePause() => Paused = !Paused;
public void TogglePause() => OnPauseChanged(Paused = !Paused);
public int SetVolume(int volume)
{
@ -181,8 +209,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()
{
@ -227,13 +273,18 @@ namespace NadekoBot.Modules.Music.Classes
});
}
public void RemoveSongAt(int index)
public void RemoveSongAt(int index, bool silent = false)
{
actionQueue.Enqueue(() =>
{
if (index < 0 || index >= playlist.Count)
return;
playlist.RemoveAt(index);
var song = playlist.ElementAtOrDefault(index);
if (playlist.Remove(song) && !silent)
{
SongRemoved(song, index);
}
});
}
@ -249,11 +300,11 @@ 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))
.Distinct();
.Distinct();
var durations = await NadekoBot.Google.GetVideoDurationsAsync(ids);
@ -263,11 +314,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,79 @@ 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
{
var time = TotalTime.ToString(@"mm\:ss");
var hrs = (int)TotalTime.TotalHours;
if (hrs > 0)
return hrs + ":" + time;
else
return time;
}
}
}
public string Thumbnail {
get {
switch (SongInfo.ProviderType)
{
case MusicType.Radio:
return $"https://cdn.discordapp.com/attachments/155726317222887425/261850925063340032/1482522097_radio.png"; //test links
case MusicType.Normal:
//todo have videoid in songinfo from the start
var videoId = Regex.Match(SongInfo.Query, "<=v=[a-zA-Z0-9-]+(?=&)|(?<=[0-9])[^&\n]+|(?<=v=)[^&\n]+");
return $"https://img.youtube.com/vi/{ videoId }/0.jpg";
case MusicType.Local:
return $"https://cdn.discordapp.com/attachments/155726317222887425/261850914783100928/1482522077_music.png"; //test links
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 +121,7 @@ namespace NadekoBot.Modules.Music.Classes
}
}
public TimeSpan TotalLength { get; set; } = TimeSpan.Zero;
private readonly Logger _log;
public Song(SongInfo songInfo)
{
@ -79,16 +133,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,11 +144,9 @@ 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;
var attempt = 0;
var prebufferingTask = CheckPrebufferingAsync(inStream, cancelToken, 1.MiB()); //Fast connection can do this easy
var finished = false;
@ -121,7 +167,7 @@ namespace NadekoBot.Modules.Music.Classes
_log.Warn("Slow connection buffering more to ensure no disruption, consider hosting in cloud");
continue;
}
if (inStream.BufferingCompleted && count == 1)
{
_log.Debug("Prebuffering canceled. Cannot get any data from the stream.");
@ -131,7 +177,7 @@ namespace NadekoBot.Modules.Music.Classes
{
continue;
}
}
}
else if (prebufferingTask.IsCanceled)
{
_log.Debug("Prebuffering canceled. Cannot get any data from the stream.");
@ -140,19 +186,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 +219,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 +236,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 +254,7 @@ namespace NadekoBot.Modules.Music.Classes
finally
{
await bufferTask;
if(inStream != null)
if (inStream != null)
inStream.Dispose();
}
}
@ -213,35 +268,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 +293,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,8 +109,8 @@ Check the guides for your platform on how to setup ffmpeg correctly:
}
finally
{
if(outStream != null)
outStream.Dispose();
if (outStream != null)
outStream.Dispose();
Console.WriteLine($"Buffering done.");
if (p != null)
{
@ -130,7 +131,7 @@ Check the guides for your platform on how to setup ffmpeg correctly:
private string GetNextFile()
{
string filename = Basename + "-" + NextFileToRead;
if (NextFileToRead != 0)
{
try
@ -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())
{
@ -215,4 +216,4 @@ Check the guides for your platform on how to setup ffmpeg correctly:
base.Dispose();
}
}
}
}

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}";
}

File diff suppressed because it is too large Load Diff

View File

@ -10,29 +10,26 @@ using System.Net.Http;
using System.Text.RegularExpressions;
using System.Xml.Linq;
using NadekoBot.Extensions;
using System.Xml;
using System.Threading;
using System.Collections.Concurrent;
namespace NadekoBot.Modules.NSFW
{
[NadekoModule("NSFW", "~")]
public class NSFW : DiscordModule
{
public NSFW(ILocalization loc, CommandService cmds, ShardedDiscordClient client) : base(loc, cmds, client)
{
}
private static ConcurrentDictionary<ulong, Timer> AutoHentaiTimers { get; } = new ConcurrentDictionary<ulong, Timer>();
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Hentai(IUserMessage umsg, [Remainder] string tag = null)
private async Task InternalHentai(IMessageChannel channel, string tag, bool noError)
{
var channel = (ITextChannel)umsg.Channel;
tag = tag?.Trim() ?? "";
tag = "rating%3Aexplicit+" + tag;
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,280 +48,230 @@ namespace NadekoBot.Modules.NSFW
}
var link = await provider.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 async Task HentaiBomb(IUserMessage umsg, [Remainder] string tag = null)
{
var channel = (ITextChannel)umsg.Channel;
tag = tag?.Trim() ?? "";
tag = "rating%3Aexplicit+" + tag;
var links = await Task.WhenAll(GetGelbooruImageLink(tag),
GetDanbooruImageLink(tag),
GetKonachanImageLink(tag),
GetYandereImageLink(tag)).ConfigureAwait(false);
if (links.All(l => l == null))
{
await channel.SendMessageAsync("`No results.`").ConfigureAwait(false);
if (!noError)
await channel.SendErrorAsync("No results found.").ConfigureAwait(false);
return;
}
await channel.SendMessageAsync(String.Join("\n\n", links)).ConfigureAwait(false);
await channel.EmbedAsync(new EmbedBuilder().WithOkColor()
.WithImageUrl(link)
.WithDescription("Tag: " + tag)).ConfigureAwait(false);
}
public static async Task<string> GetYandereImageLink(string tag)
[NadekoCommand, Usage, Description, Aliases]
public Task Hentai([Remainder] string tag = null) =>
InternalHentai(Context.Channel, tag, false);
[NadekoCommand, Usage, Description, Aliases]
public async Task AutoHentai(int interval = 0, string tags = null)
{
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())
Timer t;
if (interval == 0)
{
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;
if (AutoHentaiTimers.TryRemove(Context.Channel.Id, out t))
{
t.Change(Timeout.Infinite, Timeout.Infinite); //proper way to disable the timer
await Context.Channel.SendConfirmAsync("Autohentai stopped.").ConfigureAwait(false);
}
return;
}
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Yandere(IUserMessage umsg, [Remainder] string tag = null)
{
var channel = (ITextChannel)umsg.Channel;
if (interval < 20)
return;
var tagsArr = tags?.Split('|');
t = new Timer(async (state) =>
{
try
{
if (tagsArr == null || tagsArr.Length == 0)
await InternalHentai(Context.Channel, null, true).ConfigureAwait(false);
else
await InternalHentai(Context.Channel, tagsArr[new NadekoRandom().Next(0, tagsArr.Length)], true);
}
catch { }
}, null, interval * 1000, interval * 1000);
AutoHentaiTimers.AddOrUpdate(Context.Channel.Id, t, (key, old) =>
{
old.Change(Timeout.Infinite, Timeout.Infinite);
return t;
});
await Context.Channel.SendConfirmAsync($"Autohentai started. Reposting every {interval}s with one of the following tags:\n{string.Join(", ", tagsArr)}").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
public async Task HentaiBomb([Remainder] string tag = null)
{
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);
tag = "rating%3Aexplicit+" + tag;
var links = await Task.WhenAll(GetGelbooruImageLink(tag),
GetDanbooruImageLink(tag),
GetKonachanImageLink(tag),
GetYandereImageLink(tag)).ConfigureAwait(false);
var linksEnum = links?.Where(l => l != null);
if (links == null || !linksEnum.Any())
{
await Context.Channel.SendErrorAsync("No results found.").ConfigureAwait(false);
return;
}
await Context.Channel.SendMessageAsync(String.Join("\n\n", linksEnum)).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Danbooru(IUserMessage umsg, [Remainder] string tag = null)
{
var channel = (ITextChannel)umsg.Channel;
[NadekoCommand, Usage, Description, Aliases]
public async Task Danbooru([Remainder] string tag = null)
{
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 Context.Channel.SendErrorAsync(Context.User.Mention + " No results.");
else
await channel.SendMessageAsync(link).ConfigureAwait(false);
await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
.WithDescription(Context.User.Mention + " " + tag)
.WithImageUrl(url)
.WithFooter(efb => efb.WithText("Danbooru"))).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Konachan(IUserMessage umsg, [Remainder] string tag = null)
{
var channel = (ITextChannel)umsg.Channel;
public Task Yandere([Remainder] string tag = null)
=> Searches.Searches.InternalDapiCommand(Context.Message, tag, Searches.Searches.DapiSearchType.Yandere);
[NadekoCommand, Usage, Description, Aliases]
public Task Konachan([Remainder] string tag = null)
=> Searches.Searches.InternalDapiCommand(Context.Message, tag, Searches.Searches.DapiSearchType.Konachan);
[NadekoCommand, Usage, Description, Aliases]
public Task Gelbooru([Remainder] string tag = null)
=> Searches.Searches.InternalDapiCommand(Context.Message, tag, Searches.Searches.DapiSearchType.Gelbooru);
[NadekoCommand, Usage, Description, Aliases]
public Task Rule34([Remainder] string tag = null)
=> Searches.Searches.InternalDapiCommand(Context.Message, tag, Searches.Searches.DapiSearchType.Rule34);
[NadekoCommand, Usage, Description, Aliases]
public async Task E621([Remainder] string tag = null)
{
tag = tag?.Trim() ?? "";
var link = await GetKonachanImageLink(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 Context.Channel.SendErrorAsync(Context.User.Mention + " No results.");
else
await channel.SendMessageAsync(link).ConfigureAwait(false);
await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
.WithDescription(Context.User.Mention + " " + tag)
.WithImageUrl(url)
.WithFooter(efb => efb.WithText("e621"))).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Gelbooru(IUserMessage umsg, [Remainder] string tag = null)
public async Task Cp()
{
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);
await Context.Channel.SendMessageAsync("http://i.imgur.com/MZkY1md.jpg").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Rule34(IUserMessage umsg, [Remainder] string tag = null)
public async Task Boobs()
{
var channel = (ITextChannel)umsg.Channel;
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 async Task E621(IUserMessage umsg, [Remainder] string tag = null)
{
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);
else
await channel.SendMessageAsync(link).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Cp(IUserMessage umsg)
{
var channel = (ITextChannel)umsg.Channel;
await channel.SendMessageAsync("http://i.imgur.com/MZkY1md.jpg").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Boobs(IUserMessage umsg)
{
var channel = (ITextChannel)umsg.Channel;
try
{
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);
await Context.Channel.SendMessageAsync($"http://media.oboobs.ru/{ obj["preview"].ToString() }").ConfigureAwait(false);
}
catch (Exception ex)
{
await channel.SendMessageAsync($"💢 {ex.Message}").ConfigureAwait(false);
await Context.Channel.SendErrorAsync(ex.Message).ConfigureAwait(false);
}
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Butts(IUserMessage umsg)
public async Task Butts()
{
var channel = (ITextChannel)umsg.Channel;
try
{
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);
await Context.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 Context.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;
@ -20,59 +21,84 @@ namespace NadekoBot.Modules.Permissions
}
[Group]
public class BlacklistCommands
public class BlacklistCommands : ModuleBase
{
public static ConcurrentHashSet<BlacklistItem> BlacklistedItems { get; set; } = new ConcurrentHashSet<BlacklistItem>();
public static ConcurrentHashSet<ulong> BlacklistedUsers { get; set; } = new ConcurrentHashSet<ulong>();
public static ConcurrentHashSet<ulong> BlacklistedGuilds { get; set; } = new ConcurrentHashSet<ulong>();
public static ConcurrentHashSet<ulong> BlacklistedChannels { get; set; } = new ConcurrentHashSet<ulong>();
static BlacklistCommands()
{
using (var uow = DbHandler.UnitOfWork())
{
BlacklistedItems = new ConcurrentHashSet<BlacklistItem>(uow.BotConfig.GetOrCreate().Blacklist);
var blacklist = uow.BotConfig.GetOrCreate().Blacklist;
BlacklistedUsers = new ConcurrentHashSet<ulong>(blacklist.Where(bi => bi.Type == BlacklistType.User).Select(c => c.ItemId));
BlacklistedGuilds = new ConcurrentHashSet<ulong>(blacklist.Where(bi => bi.Type == BlacklistType.Server).Select(c => c.ItemId));
BlacklistedChannels = new ConcurrentHashSet<ulong>(blacklist.Where(bi => bi.Type == BlacklistType.Channel).Select(c => c.ItemId));
}
}
[NadekoCommand, Usage, Description, Aliases]
[OwnerOnly]
public Task UserBlacklist(IUserMessage imsg, AddRemove action, ulong id)
=> Blacklist(imsg, action, id, BlacklistType.User);
public Task UserBlacklist(AddRemove action, ulong id)
=> Blacklist(action, id, BlacklistType.User);
[NadekoCommand, Usage, Description, Aliases]
[OwnerOnly]
public Task UserBlacklist(IUserMessage imsg, AddRemove action, IUser usr)
=> Blacklist(imsg, action, usr.Id, BlacklistType.User);
public Task UserBlacklist(AddRemove action, IUser usr)
=> Blacklist(action, usr.Id, BlacklistType.User);
[NadekoCommand, Usage, Description, Aliases]
[OwnerOnly]
public Task ChannelBlacklist(IUserMessage imsg, AddRemove action, ulong id)
=> Blacklist(imsg, action, id, BlacklistType.Channel);
public Task ChannelBlacklist(AddRemove action, ulong id)
=> Blacklist(action, id, BlacklistType.Channel);
[NadekoCommand, Usage, Description, Aliases]
[OwnerOnly]
public Task ServerBlacklist(IUserMessage imsg, AddRemove action, ulong id)
=> Blacklist(imsg, action, id, BlacklistType.Server);
public Task ServerBlacklist(AddRemove action, ulong id)
=> Blacklist(action, id, BlacklistType.Server);
[NadekoCommand, Usage, Description, Aliases]
[OwnerOnly]
public Task ServerBlacklist(IUserMessage imsg, AddRemove action, IGuild guild)
=> Blacklist(imsg, action, guild.Id, BlacklistType.Server);
public Task ServerBlacklist(AddRemove action, IGuild guild)
=> Blacklist(action, guild.Id, BlacklistType.Server);
private async Task Blacklist(IUserMessage imsg, AddRemove action, ulong id, BlacklistType type)
private async Task Blacklist(AddRemove action, ulong id, BlacklistType type)
{
var channel = imsg.Channel;
using (var uow = DbHandler.UnitOfWork())
{
if (action == AddRemove.Add)
{
var item = new BlacklistItem { ItemId = id, Type = type };
uow.BotConfig.GetOrCreate().Blacklist.Add(item);
BlacklistedItems.Add(item);
if (type == BlacklistType.Server)
{
BlacklistedGuilds.Add(id);
}
else if (type == BlacklistType.Channel)
{
BlacklistedChannels.Add(id);
}
else if (type == BlacklistType.User)
{
BlacklistedUsers.Add(id);
}
}
else
{
uow.BotConfig.GetOrCreate().Blacklist.RemoveWhere(bi => bi.ItemId == id && bi.Type == type);
BlacklistedItems.RemoveWhere(bi => bi.ItemId == id && bi.Type == type);
if (type == BlacklistType.Server)
{
BlacklistedGuilds.TryRemove(id);
}
else if (type == BlacklistType.Channel)
{
BlacklistedChannels.TryRemove(id);
}
else if (type == BlacklistType.User)
{
BlacklistedUsers.TryRemove(id);
}
}
await uow.CompleteAsync().ConfigureAwait(false);
}
@ -104,7 +130,10 @@ namespace NadekoBot.Modules.Permissions
}
await channel.SendMessageAsync(":ok:").ConfigureAwait(false);
if(action == AddRemove.Add)
await Context.Channel.SendConfirmAsync($"Blacklisted a `{type}` with id `{id}`").ConfigureAwait(false);
else
await Context.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;
@ -20,42 +21,39 @@ namespace NadekoBot.Modules.Permissions
}
[Group]
public class CmdCdsCommands
public class CmdCdsCommands : ModuleBase
{
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()
{
using (var uow = DbHandler.UnitOfWork())
{
var configs = NadekoBot.AllGuildConfigs;
commandCooldowns = new ConcurrentDictionary<ulong, ConcurrentHashSet<CommandCooldown>>(configs.ToDictionary(k => k.GuildId, v => new ConcurrentHashSet<CommandCooldown>(v.CommandCooldowns)));
}
var configs = NadekoBot.AllGuildConfigs;
commandCooldowns = new ConcurrentDictionary<ulong, ConcurrentHashSet<CommandCooldown>>(configs.ToDictionary(k => k.GuildId, v => new ConcurrentHashSet<CommandCooldown>(v.CommandCooldowns)));
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task CmdCooldown(IUserMessage imsg, Command command, int secs)
public async Task CmdCooldown(CommandInfo command, int secs)
{
var channel = (ITextChannel)imsg.Channel;
var channel = (ITextChannel)Context.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());
localSet.RemoveWhere(cc => cc.CommandName == command.Text.ToLowerInvariant());
config.CommandCooldowns.RemoveWhere(cc => cc.CommandName == command.Aliases.First().ToLowerInvariant());
localSet.RemoveWhere(cc => cc.CommandName == command.Aliases.First().ToLowerInvariant());
if (secs != 0)
{
var cc = new CommandCooldown()
{
CommandName = command.Text.ToLowerInvariant(),
CommandName = command.Aliases.First().ToLowerInvariant(),
Seconds = secs,
};
config.CommandCooldowns.Add(cc);
@ -66,36 +64,40 @@ namespace NadekoBot.Modules.Permissions
if (secs == 0)
{
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);
activeCds.RemoveWhere(ac => ac.Command == command.Aliases.First().ToLowerInvariant());
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]
[RequireContext(ContextType.Guild)]
public async Task AllCmdCooldowns(IUserMessage imsg)
public async Task AllCmdCooldowns()
{
var channel = (ITextChannel)imsg.Channel;
var channel = (ITextChannel)Context.Channel;
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);
}
public static bool HasCooldown(Command cmd, IGuild guild, IUser user)
public static bool HasCooldown(CommandInfo cmd, IGuild guild, IUser user)
{
if (guild == null)
return false;
var cmdcds = CmdCdsCommands.commandCooldowns.GetOrAdd(guild.Id, new ConcurrentHashSet<CommandCooldown>());
CommandCooldown cdRule;
if ((cdRule = cmdcds.FirstOrDefault(cc => cc.CommandName == cmd.Text.ToLowerInvariant())) != null)
if ((cdRule = cmdcds.FirstOrDefault(cc => cc.CommandName == cmd.Aliases.First().ToLowerInvariant())) != null)
{
var activeCdsForGuild = activeCooldowns.GetOrAdd(guild.Id, new ConcurrentHashSet<ActiveCooldown>());
if (activeCdsForGuild.FirstOrDefault(ac => ac.UserId == user.Id && ac.Command == cmd.Text.ToLowerInvariant()) != null)
if (activeCdsForGuild.FirstOrDefault(ac => ac.UserId == user.Id && ac.Command == cmd.Aliases.First().ToLowerInvariant()) != null)
{
return true;
}
@ -104,14 +106,14 @@ namespace NadekoBot.Modules.Permissions
activeCdsForGuild.Add(new ActiveCooldown()
{
UserId = user.Id,
Command = cmd.Text.ToLowerInvariant(),
Command = cmd.Aliases.First().ToLowerInvariant(),
});
var t = Task.Run(async () =>
{
try
{
await Task.Delay(cdRule.Seconds * 1000);
activeCdsForGuild.RemoveWhere(ac => ac.Command == cmd.Text.ToLowerInvariant() && ac.UserId == user.Id);
activeCdsForGuild.RemoveWhere(ac => ac.Command == cmd.Aliases.First().ToLowerInvariant() && ac.UserId == user.Id);
}
catch { }
});

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;
@ -11,16 +13,16 @@ namespace NadekoBot.Modules.Permissions
public partial class Permissions
{
[Group]
public class FilterCommands
public class FilterCommands : ModuleBase
{
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)
{
@ -61,14 +63,14 @@ namespace NadekoBot.Modules.Permissions
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task SrvrFilterInv(IUserMessage imsg)
public async Task SrvrFilterInv()
{
var channel = (ITextChannel)imsg.Channel;
var channel = (ITextChannel)Context.Channel;
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,25 +78,25 @@ 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);
}
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task ChnlFilterInv(IUserMessage imsg)
public async Task ChnlFilterInv()
{
var channel = (ITextChannel)imsg.Channel;
var channel = (ITextChannel)Context.Channel;
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,25 +111,25 @@ 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);
}
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task SrvrFilterWords(IUserMessage imsg)
public async Task SrvrFilterWords()
{
var channel = (ITextChannel)imsg.Channel;
var channel = (ITextChannel)Context.Channel;
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,25 +137,25 @@ 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);
}
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task ChnlFilterWords(IUserMessage imsg)
public async Task ChnlFilterWords()
{
var channel = (ITextChannel)imsg.Channel;
var channel = (ITextChannel)Context.Channel;
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,20 +170,20 @@ 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);
}
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task FilterWord(IUserMessage imsg, [Remainder] string word)
public async Task FilterWord([Remainder] string word)
{
var channel = (ITextChannel)imsg.Channel;
var channel = (ITextChannel)Context.Channel;
word = word?.Trim().ToLowerInvariant();
@ -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,27 +208,27 @@ 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);
}
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task LstFilterWords(IUserMessage imsg)
public async Task LstFilterWords()
{
var channel = (ITextChannel)imsg.Channel;
var channel = (ITextChannel)Context.Channel;
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

@ -10,7 +10,7 @@ namespace NadekoBot.Modules.Permissions
{
public static class PermissionExtensions
{
public static bool CheckPermissions(this IEnumerable<Permission> permsEnumerable, IUserMessage message, Command command)
public static bool CheckPermissions(this IEnumerable<Permission> permsEnumerable, IUserMessage message, CommandInfo command)
{
var perms = permsEnumerable as List<Permission> ?? permsEnumerable.ToList();
int throwaway;
@ -75,7 +75,7 @@ namespace NadekoBot.Modules.Permissions
case PrimaryPermissionType.Role:
if (guildUser == null)
break;
if (guildUser.Roles.Any(r => r.Id == perm.PrimaryTargetId))
if (guildUser.RoleIds.Contains(perm.PrimaryTargetId))
return perm.State;
break;
case PrimaryPermissionType.Server:
@ -86,7 +86,7 @@ namespace NadekoBot.Modules.Permissions
return null;
}
public static string GetCommand(this Permission perm, IGuild guild = null)
public static string GetCommand(this Permission perm, SocketGuild guild = null)
{
var com = "";
switch (perm.PrimaryTarget)

View File

@ -7,6 +7,10 @@ using NadekoBot.Services;
using Discord;
using NadekoBot.Services.Database.Models;
using System.Collections.Concurrent;
using NadekoBot.Extensions;
using Discord.WebSocket;
using System.Diagnostics;
using NLog;
namespace NadekoBot.Modules.Permissions
{
@ -21,10 +25,13 @@ namespace NadekoBot.Modules.Permissions
}
//guildid, root permission
public static ConcurrentDictionary<ulong, PermissionCache> Cache;
public static ConcurrentDictionary<ulong, PermissionCache> Cache { get; }
static Permissions()
{
var _log = LogManager.GetCurrentClassLogger();
var sw = Stopwatch.StartNew();
using (var uow = DbHandler.UnitOfWork())
{
Cache = new ConcurrentDictionary<ulong, PermissionCache>(uow.GuildConfigs
@ -37,23 +44,20 @@ namespace NadekoBot.Modules.Permissions
PermRole = v.PermissionRole
}));
}
}
public Permissions(ILocalization loc, CommandService cmds, ShardedDiscordClient client) : base(loc, cmds, client)
{
sw.Stop();
_log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s");
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Verbose(IUserMessage msg, PermissionAction action)
public async Task Verbose(PermissionAction action)
{
var channel = (ITextChannel)msg.Channel;
using (var uow = DbHandler.UnitOfWork())
{
var config = uow.GuildConfigs.For(channel.Guild.Id);
var config = uow.GuildConfigs.For(Context.Guild.Id, set => set);
config.VerbosePermissions = action.Value;
Cache.AddOrUpdate(channel.Guild.Id, new PermissionCache()
Cache.AddOrUpdate(Context.Guild.Id, new PermissionCache()
{
PermRole = config.PermissionRole,
RootPermission = Permission.GetDefaultRoot(),
@ -62,25 +66,27 @@ 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 Context.Channel.SendConfirmAsync(" I will " + (action.Value ? "now" : "no longer") + " show permission warnings.").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task PermRole(IUserMessage msg, [Remainder] IRole role = null)
public async Task PermRole([Remainder] IRole role = null)
{
var channel = (ITextChannel)msg.Channel;
if (role != null && role == role.Guild.EveryoneRole)
return;
using (var uow = DbHandler.UnitOfWork())
{
var config = uow.GuildConfigs.For(channel.Guild.Id);
var config = uow.GuildConfigs.For(Context.Guild.Id, set => set);
if (role == null)
{
await channel.SendMessageAsync($" Current permission role is **{config.PermissionRole}**.").ConfigureAwait(false);
await Context.Channel.SendConfirmAsync($" Current permission role is **{config.PermissionRole}**.").ConfigureAwait(false);
return;
}
else {
config.PermissionRole = role.Name.Trim();
Cache.AddOrUpdate(channel.Guild.Id, new PermissionCache()
Cache.AddOrUpdate(Context.Guild.Id, new PermissionCache()
{
PermRole = config.PermissionRole,
RootPermission = Permission.GetDefaultRoot(),
@ -90,43 +96,37 @@ namespace NadekoBot.Modules.Permissions
}
}
await channel.SendMessageAsync($"✅ Users now require **{role.Name}** role in order to edit permissions.").ConfigureAwait(false);
await Context.Channel.SendConfirmAsync($"Users now require **{role.Name}** role in order to edit permissions.").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task ListPerms(IUserMessage msg, int page = 1)
public async Task ListPerms(int page = 1)
{
var channel = (ITextChannel)msg.Channel;
if (page < 1 || page > 4)
return;
string toSend = "";
using (var uow = DbHandler.UnitOfWork())
{
var perms = uow.GuildConfigs.PermissionsFor(channel.Guild.Id).RootPermission;
var perms = uow.GuildConfigs.PermissionsFor(Context.Guild.Id).RootPermission;
var i = 1 + 20 * (page - 1);
toSend = Format.Code($"📄 Permissions page {page}") + "\n\n" + String.Join("\n", perms.AsEnumerable().Skip((page - 1) * 20).Take(20).Select(p => $"`{(i++)}.` {(p.Next == null ? Format.Bold(p.GetCommand(channel.Guild) + " [uneditable]") : (p.GetCommand(channel.Guild)))}"));
toSend = Format.Code($"📄 Permissions page {page}") + "\n\n" + String.Join("\n", perms.AsEnumerable().Skip((page - 1) * 20).Take(20).Select(p => $"`{(i++)}.` {(p.Next == null ? Format.Bold(p.GetCommand((SocketGuild)Context.Guild) + " [uneditable]") : (p.GetCommand((SocketGuild)Context.Guild)))}"));
}
if (string.IsNullOrWhiteSpace(toSend))
await channel.SendMessageAsync("❗️`No permissions set.`").ConfigureAwait(false);
else
await channel.SendMessageAsync(toSend).ConfigureAwait(false);
await Context.Channel.SendMessageAsync(toSend).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task RemovePerm(IUserMessage imsg, int index)
public async Task RemovePerm(int index)
{
var channel = (ITextChannel)imsg.Channel;
index -= 1;
try
{
Permission p;
using (var uow = DbHandler.UnitOfWork())
{
var config = uow.GuildConfigs.PermissionsFor(channel.Guild.Id);
var config = uow.GuildConfigs.PermissionsFor(Context.Guild.Id);
var perms = config.RootPermission;
if (index == perms.Count() - 1)
{
@ -141,7 +141,7 @@ namespace NadekoBot.Modules.Permissions
{
p = perms.RemoveAt(index);
}
Cache.AddOrUpdate(channel.Guild.Id, new PermissionCache()
Cache.AddOrUpdate(Context.Guild.Id, new PermissionCache()
{
PermRole = config.PermissionRole,
RootPermission = config.RootPermission,
@ -156,21 +156,20 @@ 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 Context.Channel.SendConfirmAsync($"✅ {Context.User.Mention} removed permission **{p.GetCommand((SocketGuild)Context.Guild)}** from position #{index + 1}.").ConfigureAwait(false);
}
catch (ArgumentOutOfRangeException)
{
await channel.SendMessageAsync("❗️`No command on that index found.`").ConfigureAwait(false);
await Context.Channel.SendErrorAsync("❗️`No command on that index found.`").ConfigureAwait(false);
}
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task MovePerm(IUserMessage imsg, int from, int to)
public async Task MovePerm(int from, int to)
{
from -= 1;
to -= 1;
var channel = (ITextChannel)imsg.Channel;
if (!(from == to || from < 0 || to < 0))
{
try
@ -179,7 +178,7 @@ namespace NadekoBot.Modules.Permissions
Permission toPerm = null;
using (var uow = DbHandler.UnitOfWork())
{
var config = uow.GuildConfigs.PermissionsFor(channel.Guild.Id);
var config = uow.GuildConfigs.PermissionsFor(Context.Guild.Id);
var perms = config.RootPermission;
var root = perms;
var index = 0;
@ -208,13 +207,13 @@ namespace NadekoBot.Modules.Permissions
{
if (!fromFound)
{
await channel.SendMessageAsync($"❗️`Can't find permission at index `#{++from}`").ConfigureAwait(false);
await Context.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 Context.Channel.SendErrorAsync($"Can't find permission at index `#{++to}`").ConfigureAwait(false);
return;
}
}
@ -256,7 +255,7 @@ namespace NadekoBot.Modules.Permissions
}
config.RootPermission = fromPerm.GetRoot();
Cache.AddOrUpdate(channel.Guild.Id, new PermissionCache()
Cache.AddOrUpdate(Context.Guild.Id, new PermissionCache()
{
PermRole = config.PermissionRole,
RootPermission = config.RootPermission,
@ -264,22 +263,20 @@ 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 Context.Channel.SendConfirmAsync($"`Moved permission:` \"{fromPerm.GetCommand((SocketGuild)Context.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 Context.Channel.SendErrorAsync("`Invalid index(es) specified.`").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task SrvrCmd(IUserMessage imsg, Command command, PermissionAction action)
public async Task SrvrCmd(CommandInfo command, PermissionAction action)
{
var channel = (ITextChannel)imsg.Channel;
using (var uow = DbHandler.UnitOfWork())
{
var newPerm = new Permission
@ -287,11 +284,11 @@ namespace NadekoBot.Modules.Permissions
PrimaryTarget = PrimaryPermissionType.Server,
PrimaryTargetId = 0,
SecondaryTarget = SecondaryPermissionType.Command,
SecondaryTargetName = command.Text.ToLowerInvariant(),
SecondaryTargetName = command.Aliases.First().ToLowerInvariant(),
State = action.Value,
};
var config = uow.GuildConfigs.SetNewRootPermission(channel.Guild.Id, newPerm);
Cache.AddOrUpdate(channel.Guild.Id, new PermissionCache()
var config = uow.GuildConfigs.SetNewRootPermission(Context.Guild.Id, newPerm);
Cache.AddOrUpdate(Context.Guild.Id, new PermissionCache()
{
PermRole = config.PermissionRole,
RootPermission = config.RootPermission,
@ -300,15 +297,13 @@ 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 Context.Channel.SendConfirmAsync($"{(action.Value ? " Allowed" : "🆗 Denied")} usage of `{command.Aliases.First()}` command on this server.").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task SrvrMdl(IUserMessage imsg, Module module, PermissionAction action)
public async Task SrvrMdl(ModuleInfo module, PermissionAction action)
{
var channel = (ITextChannel)imsg.Channel;
using (var uow = DbHandler.UnitOfWork())
{
var newPerm = new Permission
@ -319,8 +314,8 @@ namespace NadekoBot.Modules.Permissions
SecondaryTargetName = module.Name.ToLowerInvariant(),
State = action.Value,
};
var config = uow.GuildConfigs.SetNewRootPermission(channel.Guild.Id, newPerm);
Cache.AddOrUpdate(channel.Guild.Id, new PermissionCache()
var config = uow.GuildConfigs.SetNewRootPermission(Context.Guild.Id, newPerm);
Cache.AddOrUpdate(Context.Guild.Id, new PermissionCache()
{
PermRole = config.PermissionRole,
RootPermission = config.RootPermission,
@ -328,15 +323,13 @@ 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 Context.Channel.SendConfirmAsync($"{(action.Value ? " Allowed" : "🆗 Denied")} usage of **`{module.Name}`** module on this server.").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task UsrCmd(IUserMessage imsg, Command command, PermissionAction action, [Remainder] IGuildUser user)
public async Task UsrCmd(CommandInfo command, PermissionAction action, [Remainder] IGuildUser user)
{
var channel = (ITextChannel)imsg.Channel;
using (var uow = DbHandler.UnitOfWork())
{
var newPerm = new Permission
@ -344,11 +337,11 @@ namespace NadekoBot.Modules.Permissions
PrimaryTarget = PrimaryPermissionType.User,
PrimaryTargetId = user.Id,
SecondaryTarget = SecondaryPermissionType.Command,
SecondaryTargetName = command.Text.ToLowerInvariant(),
SecondaryTargetName = command.Aliases.First().ToLowerInvariant(),
State = action.Value,
};
var config = uow.GuildConfigs.SetNewRootPermission(channel.Guild.Id, newPerm);
Cache.AddOrUpdate(channel.Guild.Id, new PermissionCache()
var config = uow.GuildConfigs.SetNewRootPermission(Context.Guild.Id, newPerm);
Cache.AddOrUpdate(Context.Guild.Id, new PermissionCache()
{
PermRole = config.PermissionRole,
RootPermission = config.RootPermission,
@ -356,15 +349,13 @@ 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 Context.Channel.SendConfirmAsync($"{(action.Value ? " Allowed" : "🆗 Denied")} usage of `{command.Aliases.First()}` command for `{user}` user.").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task UsrMdl(IUserMessage imsg, Module module, PermissionAction action, [Remainder] IGuildUser user)
public async Task UsrMdl(ModuleInfo module, PermissionAction action, [Remainder] IGuildUser user)
{
var channel = (ITextChannel)imsg.Channel;
using (var uow = DbHandler.UnitOfWork())
{
var newPerm = new Permission
@ -375,8 +366,8 @@ namespace NadekoBot.Modules.Permissions
SecondaryTargetName = module.Name.ToLowerInvariant(),
State = action.Value,
};
var config = uow.GuildConfigs.SetNewRootPermission(channel.Guild.Id, newPerm);
Cache.AddOrUpdate(channel.Guild.Id, new PermissionCache()
var config = uow.GuildConfigs.SetNewRootPermission(Context.Guild.Id, newPerm);
Cache.AddOrUpdate(Context.Guild.Id, new PermissionCache()
{
PermRole = config.PermissionRole,
RootPermission = config.RootPermission,
@ -384,14 +375,15 @@ 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 Context.Channel.SendConfirmAsync($"{(action.Value ? " Allowed" : "🆗 Denied")} usage of `{module.Name}` module for `{user}` user.").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task RoleCmd(IUserMessage imsg, Command command, PermissionAction action, [Remainder] IRole role)
public async Task RoleCmd(CommandInfo command, PermissionAction action, [Remainder] IRole role)
{
var channel = (ITextChannel)imsg.Channel;
if (role == role.Guild.EveryoneRole)
return;
using (var uow = DbHandler.UnitOfWork())
{
@ -400,11 +392,11 @@ namespace NadekoBot.Modules.Permissions
PrimaryTarget = PrimaryPermissionType.Role,
PrimaryTargetId = role.Id,
SecondaryTarget = SecondaryPermissionType.Command,
SecondaryTargetName = command.Text.ToLowerInvariant(),
SecondaryTargetName = command.Aliases.First().ToLowerInvariant(),
State = action.Value,
};
var config = uow.GuildConfigs.SetNewRootPermission(channel.Guild.Id, newPerm);
Cache.AddOrUpdate(channel.Guild.Id, new PermissionCache()
var config = uow.GuildConfigs.SetNewRootPermission(Context.Guild.Id, newPerm);
Cache.AddOrUpdate(Context.Guild.Id, new PermissionCache()
{
PermRole = config.PermissionRole,
RootPermission = config.RootPermission,
@ -412,14 +404,15 @@ 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 Context.Channel.SendConfirmAsync($"{(action.Value ? " Allowed" : "🆗 Denied")} usage of `{command.Aliases.First()}` command for `{role}` role.").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task RoleMdl(IUserMessage imsg, Module module, PermissionAction action, [Remainder] IRole role)
public async Task RoleMdl(ModuleInfo module, PermissionAction action, [Remainder] IRole role)
{
var channel = (ITextChannel)imsg.Channel;
if (role == role.Guild.EveryoneRole)
return;
using (var uow = DbHandler.UnitOfWork())
{
@ -431,8 +424,8 @@ namespace NadekoBot.Modules.Permissions
SecondaryTargetName = module.Name.ToLowerInvariant(),
State = action.Value,
};
var config = uow.GuildConfigs.SetNewRootPermission(channel.Guild.Id, newPerm);
Cache.AddOrUpdate(channel.Guild.Id, new PermissionCache()
var config = uow.GuildConfigs.SetNewRootPermission(Context.Guild.Id, newPerm);
Cache.AddOrUpdate(Context.Guild.Id, new PermissionCache()
{
PermRole = config.PermissionRole,
RootPermission = config.RootPermission,
@ -440,14 +433,13 @@ 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 Context.Channel.SendConfirmAsync($"{(action.Value ? " Allowed" : "🆗 Denied")} usage of `{module.Name}` module for `{role}` role.").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task ChnlCmd(IUserMessage imsg, Command command, PermissionAction action, [Remainder] ITextChannel chnl)
public async Task ChnlCmd(CommandInfo command, PermissionAction action, [Remainder] ITextChannel chnl)
{
var channel = (ITextChannel)imsg.Channel;
try
{
using (var uow = DbHandler.UnitOfWork())
@ -457,11 +449,11 @@ namespace NadekoBot.Modules.Permissions
PrimaryTarget = PrimaryPermissionType.Channel,
PrimaryTargetId = chnl.Id,
SecondaryTarget = SecondaryPermissionType.Command,
SecondaryTargetName = command.Text.ToLowerInvariant(),
SecondaryTargetName = command.Aliases.First().ToLowerInvariant(),
State = action.Value,
};
var config = uow.GuildConfigs.SetNewRootPermission(channel.Guild.Id, newPerm);
Cache.AddOrUpdate(channel.Guild.Id, new PermissionCache()
var config = uow.GuildConfigs.SetNewRootPermission(Context.Guild.Id, newPerm);
Cache.AddOrUpdate(Context.Guild.Id, new PermissionCache()
{
PermRole = config.PermissionRole,
RootPermission = config.RootPermission,
@ -471,17 +463,15 @@ 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 Context.Channel.SendConfirmAsync($"{(action.Value ? " Allowed" : "🆗 Denied")} usage of `{command.Aliases.First()}` command for `{chnl}` channel.").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task ChnlMdl(IUserMessage imsg, Module module, PermissionAction action, [Remainder] ITextChannel chnl)
public async Task ChnlMdl(ModuleInfo module, PermissionAction action, [Remainder] ITextChannel chnl)
{
var channel = (ITextChannel)imsg.Channel;
using (var uow = DbHandler.UnitOfWork())
{
var newPerm = new Permission
@ -492,8 +482,8 @@ namespace NadekoBot.Modules.Permissions
SecondaryTargetName = module.Name.ToLowerInvariant(),
State = action.Value,
};
var config = uow.GuildConfigs.SetNewRootPermission(channel.Guild.Id, newPerm);
Cache.AddOrUpdate(channel.Guild.Id, new PermissionCache()
var config = uow.GuildConfigs.SetNewRootPermission(Context.Guild.Id, newPerm);
Cache.AddOrUpdate(Context.Guild.Id, new PermissionCache()
{
PermRole = config.PermissionRole,
RootPermission = config.RootPermission,
@ -501,15 +491,13 @@ 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 Context.Channel.SendConfirmAsync($"{(action.Value ? " Allowed" : "🆗 Denied")} usage of `{module.Name}` module for `{chnl}` channel.").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task AllChnlMdls(IUserMessage imsg, PermissionAction action, [Remainder] ITextChannel chnl)
public async Task AllChnlMdls(PermissionAction action, [Remainder] ITextChannel chnl)
{
var channel = (ITextChannel)imsg.Channel;
using (var uow = DbHandler.UnitOfWork())
{
var newPerm = new Permission
@ -520,8 +508,8 @@ namespace NadekoBot.Modules.Permissions
SecondaryTargetName = "*",
State = action.Value,
};
var config = uow.GuildConfigs.SetNewRootPermission(channel.Guild.Id, newPerm);
Cache.AddOrUpdate(channel.Guild.Id, new PermissionCache()
var config = uow.GuildConfigs.SetNewRootPermission(Context.Guild.Id, newPerm);
Cache.AddOrUpdate(Context.Guild.Id, new PermissionCache()
{
PermRole = config.PermissionRole,
RootPermission = config.RootPermission,
@ -529,14 +517,15 @@ 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 Context.Channel.SendConfirmAsync($"{(action.Value ? " Allowed" : "🆗 Denied")} usage of `ALL MODULES` for `{chnl}` channel.").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task AllRoleMdls(IUserMessage imsg, PermissionAction action, [Remainder] IRole role)
public async Task AllRoleMdls(PermissionAction action, [Remainder] IRole role)
{
var channel = (ITextChannel)imsg.Channel;
if (role == role.Guild.EveryoneRole)
return;
using (var uow = DbHandler.UnitOfWork())
{
@ -548,8 +537,8 @@ namespace NadekoBot.Modules.Permissions
SecondaryTargetName = "*",
State = action.Value,
};
var config = uow.GuildConfigs.SetNewRootPermission(channel.Guild.Id, newPerm);
Cache.AddOrUpdate(channel.Guild.Id, new PermissionCache()
var config = uow.GuildConfigs.SetNewRootPermission(Context.Guild.Id, newPerm);
Cache.AddOrUpdate(Context.Guild.Id, new PermissionCache()
{
PermRole = config.PermissionRole,
RootPermission = config.RootPermission,
@ -557,15 +546,13 @@ 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 Context.Channel.SendConfirmAsync($"{(action.Value ? " Allowed" : "🆗 Denied")} usage of `ALL MODULES` for `{role}` role.").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task AllUsrMdls(IUserMessage imsg, PermissionAction action, [Remainder] IUser user)
public async Task AllUsrMdls(PermissionAction action, [Remainder] IUser user)
{
var channel = (ITextChannel)imsg.Channel;
using (var uow = DbHandler.UnitOfWork())
{
var newPerm = new Permission
@ -576,8 +563,8 @@ namespace NadekoBot.Modules.Permissions
SecondaryTargetName = "*",
State = action.Value,
};
var config = uow.GuildConfigs.SetNewRootPermission(channel.Guild.Id, newPerm);
Cache.AddOrUpdate(channel.Guild.Id, new PermissionCache()
var config = uow.GuildConfigs.SetNewRootPermission(Context.Guild.Id, newPerm);
Cache.AddOrUpdate(Context.Guild.Id, new PermissionCache()
{
PermRole = config.PermissionRole,
RootPermission = config.RootPermission,
@ -585,15 +572,13 @@ 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 Context.Channel.SendConfirmAsync($"{(action.Value ? " Allowed" : "🆗 Denied")} usage of `ALL MODULES` for `{user}` user.").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task AllSrvrMdls(IUserMessage imsg, PermissionAction action)
public async Task AllSrvrMdls(PermissionAction action)
{
var channel = (ITextChannel)imsg.Channel;
using (var uow = DbHandler.UnitOfWork())
{
var newPerm = new Permission
@ -604,19 +589,19 @@ namespace NadekoBot.Modules.Permissions
SecondaryTargetName = "*",
State = action.Value,
};
uow.GuildConfigs.SetNewRootPermission(channel.Guild.Id, newPerm);
uow.GuildConfigs.SetNewRootPermission(Context.Guild.Id, newPerm);
var allowUser = new Permission
{
PrimaryTarget = PrimaryPermissionType.User,
PrimaryTargetId = imsg.Author.Id,
PrimaryTargetId = Context.User.Id,
SecondaryTarget = SecondaryPermissionType.AllModules,
SecondaryTargetName = "*",
State = true,
};
var config = uow.GuildConfigs.SetNewRootPermission(channel.Guild.Id, allowUser);
Cache.AddOrUpdate(channel.Guild.Id, new PermissionCache()
var config = uow.GuildConfigs.SetNewRootPermission(Context.Guild.Id, allowUser);
Cache.AddOrUpdate(Context.Guild.Id, new PermissionCache()
{
PermRole = config.PermissionRole,
RootPermission = config.RootPermission,
@ -624,7 +609,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 Context.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,373 @@
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 static new Logger _log { get; }
static Pokemon()
{
_log = LogManager.GetCurrentClassLogger();
if (File.Exists(PokemonTypesFile))
{
PokemonTypes = JsonConvert.DeserializeObject<List<PokemonType>>(File.ReadAllText(PokemonTypesFile));
}
else
{
_log.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(string move, IGuildUser targetUser = null)
{
IGuildUser user = (IGuildUser)Context.User;
if (string.IsNullOrWhiteSpace(move)) {
return;
}
if (targetUser == null)
{
await Context.Channel.SendMessageAsync("No such person.").ConfigureAwait(false);
return;
}
else if (targetUser == user)
{
await Context.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 Context.Channel.SendMessageAsync($"{user.Mention} has fainted and was not able to move!").ConfigureAwait(false);
return;
}
if (userStats.MovesMade >= 5)
{
await Context.Channel.SendMessageAsync($"{user.Mention} has used too many moves in a row and was not able to move!").ConfigureAwait(false);
return;
}
if (userStats.LastAttacked.Contains(targetUser.Id))
{
await Context.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 Context.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 Context.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 Context.Channel.SendMessageAsync(response).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Movelist()
{
IGuildUser user = (IGuildUser)Context.User;
var userType = GetPokeType(user.Id);
var movesList = userType.Moves;
var str = $"**Moves for `{userType.Name}` type.**";
foreach (string m in movesList)
{
str += $"\n{userType.Icon}{m}";
}
await Context.Channel.SendMessageAsync(str).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Heal(IGuildUser targetUser = null)
{
IGuildUser user = (IGuildUser)Context.User;
if (targetUser == null) {
await Context.Channel.SendMessageAsync("No such person.").ConfigureAwait(false);
return;
}
if (Stats.ContainsKey(targetUser.Id))
{
var targetStats = Stats[targetUser.Id];
if (targetStats.Hp == targetStats.MaxHp)
{
await Context.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 Context.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 Context.Channel.SendMessageAsync($"You revived yourself with one {CurrencySign}").ConfigureAwait(false);
}
else
{
await Context.Channel.SendMessageAsync($"{user.Mention} revived {targetUser.Mention} with one {CurrencySign}").ConfigureAwait(false);
}
return;
}
await Context.Channel.SendMessageAsync($"{user.Mention} healed {targetUser.Mention} with one {CurrencySign}").ConfigureAwait(false);
return;
}
else
{
await Context.Channel.SendMessageAsync($"{targetUser.Mention} already has full HP!").ConfigureAwait(false);
}
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Type(IGuildUser targetUser = null)
{
IGuildUser user = (IGuildUser)Context.User;
if (targetUser == null)
{
return;
}
var pType = GetPokeType(targetUser.Id);
await Context.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([Remainder] string typeTargeted = null)
{
IGuildUser user = (IGuildUser)Context.User;
var targetType = StringToPokemonType(typeTargeted);
if (targetType == null)
{
await Context.Channel.EmbedAsync(PokemonTypes.Aggregate(new EmbedBuilder().WithDescription("List of the available types:"),
(eb, pt) => eb.AddField(efb => efb.WithName(pt.Name)
.WithValue(pt.Icon)
.WithIsInline(true)))
.WithColor(NadekoBot.OkColor)).ConfigureAwait(false);
return;
}
if (targetType == GetPokeType(user.Id))
{
await Context.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 Context.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 Context.Channel.SendMessageAsync($"Set type of {user.Mention} to {typeTargeted}{targetType.Icon} for a {CurrencySign}").ConfigureAwait(false);
}
}
}

View File

@ -0,0 +1,29 @@
using System.Collections.Generic;
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

@ -1,5 +1,4 @@
using Discord;
using Discord.API;
using Discord.Commands;
using NadekoBot.Attributes;
using NadekoBot.Extensions;
@ -10,6 +9,7 @@ using NLog;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
namespace NadekoBot.Modules.Searches
@ -17,100 +17,93 @@ namespace NadekoBot.Modules.Searches
public partial class Searches
{
[Group]
public class AnimeSearchCommands
public class AnimeSearchCommands : ModuleBase
{
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]
[RequireContext(ContextType.Guild)]
public async Task Anime(IUserMessage umsg, [Remainder] string query)
public async Task Anime([Remainder] string query)
{
var channel = (ITextChannel)umsg.Channel;
if (string.IsNullOrWhiteSpace(query))
return;
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)
}
},
Color = NadekoBot.OkColor
};
await channel.EmbedAsync(embed).ConfigureAwait(false);
await Context.Channel.SendErrorAsync("Failed finding that animu.").ConfigureAwait(false);
return;
}
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 Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Manga(IUserMessage umsg, [Remainder] string query)
public async Task Manga([Remainder] string query)
{
var channel = (ITextChannel)umsg.Channel;
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)
}
},
Color = NadekoBot.OkColor
};
await Context.Channel.SendErrorAsync("Failed finding that mango.").ConfigureAwait(false);
return;
}
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 Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
}
private async Task<AnimeResult> GetAnimeData(string query)
@ -119,7 +112,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 +123,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,12 +1,13 @@
using Discord;
using Discord.Commands;
using Discord.Commands;
using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Modules.Searches.Models;
using NadekoBot.Services;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NLog;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net.Http;
@ -17,15 +18,16 @@ namespace NadekoBot.Modules.Searches
public partial class Searches
{
[Group]
public class JokeCommands
public class JokeCommands : ModuleBase
{
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"))
{
wowJokes = JsonConvert.DeserializeObject<List<WoWJoke>>(File.ReadAllText("data/wowjokes.json"));
@ -42,62 +44,58 @@ namespace NadekoBot.Modules.Searches
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Yomama(IUserMessage umsg)
public async Task Yomama()
{
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);
await Context.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()
{
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 Context.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()
{
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 Context.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()
{
var channel = (ITextChannel)umsg.Channel;
if (!wowJokes.Any())
{
await Context.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 Context.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()
{
var channel = (ITextChannel)umsg.Channel;
var rng = new NadekoRandom();
var item = magicItems[rng.Next(0, magicItems.Count)].ToString();
if (!wowJokes.Any())
{
await Context.Channel.SendErrorAsync("MagicItems not loaded.").ConfigureAwait(false);
return;
}
var item = magicItems[new NadekoRandom().Next(0, magicItems.Count)];
await channel.SendMessageAsync(item).ConfigureAwait(false);
await Context.Channel.SendConfirmAsync("✨" + item.Name, item.Description).ConfigureAwait(false);
}
}
}

View File

@ -1,13 +1,12 @@
using Discord;
using Discord.Commands;
using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Services;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
//todo drawing
@ -23,7 +22,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.",
@ -32,13 +31,8 @@ namespace NadekoBot.Modules.Searches
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Lolban(IUserMessage umsg)
public async Task Lolban()
{
var channel = (ITextChannel)umsg.Channel;
var showCount = 8;
//http://api.champion.gg/stats/champs/mostBanned?api_key=YOUR_API_TOKEN&page=1&limit=2
try
@ -50,23 +44,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 Context.Channel.EmbedAsync(eb, Format.Italics(trashTalk[new NadekoRandom().Next(0, trashTalk.Length)])).ConfigureAwait(false);
}
}
catch (Exception)
{
await channel.SendMessageAsync($":anger: `Something went wrong.`").ConfigureAwait(false);
await Context.Channel.SendMessageAsync("Something went wrong.").ConfigureAwait(false);
}
}
}
@ -115,7 +105,7 @@ namespace NadekoBot.Modules.Searches
// public override void Init(CommandGroupBuilder cgb)
// {
// cgb.CreateCommand(Module.Prefix + "lolchamp")
// cgb.CreateCommand(Module.Name + "lolchamp")
// .Description($"Shows League Of Legends champion statistics. If there are spaces/apostrophes or in the name - omit them. Optional second parameter is a role. |`{Prefix}lolchamp Riven` or `{Prefix}lolchamp Annie sup`")
// .Parameter("champ", ParameterType.Required)
// .Parameter("position", ParameterType.Unparsed)

View File

@ -1,10 +1,8 @@
using Discord.Commands;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Discord;
using System.Threading.Tasks;
using NadekoBot.Attributes;
using System.Net.Http;
@ -15,42 +13,29 @@ namespace NadekoBot.Modules.Searches
public partial class Searches
{
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Memelist(IUserMessage umsg)
public async Task Memelist()
{
var channel = (ITextChannel)umsg.Channel;
HttpClientHandler handler = new HttpClientHandler();
handler.AllowAutoRedirect = false;
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));
.Select(kvp => Path.GetFileName(kvp.Value));
await channel.SendTableAsync(data, x => $"{x,-17}", 3);
await Context.Channel.SendTableAsync(data, x => $"{x,-17}", 3).ConfigureAwait(false);
}
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Memegen(IUserMessage umsg, string meme, string topText, string botText)
public async Task Memegen(string meme, string topText, string botText)
{
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");
await Context.Channel.SendMessageAsync($"http://memegen.link/{meme}/{top}/{bot}.jpg")
.ConfigureAwait(false);
}
}
}

View File

@ -1,5 +1,4 @@
using NadekoBot.Extensions;
using System.Globalization;
namespace NadekoBot.Modules.Searches.Models
{
@ -13,6 +12,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,66 @@
using Newtonsoft.Json;
using System;
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 float pressure { get; set; }
public float 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;
@ -15,7 +18,8 @@ namespace NadekoBot.Modules.Searches.Commands.OMDB
{
var res = await http.GetStringAsync(String.Format(queryUrl,name.Trim().Replace(' ','+'))).ConfigureAwait(false);
var movie = JsonConvert.DeserializeObject<OmdbMovie>(res);
if (movie?.Title == null)
return null;
movie.Poster = await NadekoBot.Google.ShortenUrl(movie.Poster);
return movie;
}
@ -32,6 +36,16 @@ namespace NadekoBot.Modules.Searches.Commands.OMDB
public string Plot { get; set; }
public string Poster { get; set; }
public EmbedBuilder 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))
.WithImageUrl(Poster);
public override string ToString() =>
$@"`Title:` {Title}
`Year:` {Year}

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