Merge pull request #20 from Kwoth/1.9

1.9
This commit is contained in:
samvaio 2017-09-24 12:22:38 +05:30 committed by GitHub
commit 52179bf070
428 changed files with 42526 additions and 8876 deletions

1
.gitignore vendored
View File

@ -1,5 +1,6 @@
#Manually added files
patreon_rewards.json
command_errors*.txt
src/NadekoBot/Command Errors*.txt

View File

@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26430.6
VisualStudioVersion = 15.0.26730.3
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{04929013-5BAB-42B0-B9B2-8F2BB8F16AF2}"
EndProject
@ -33,4 +33,7 @@ Global
GlobalSection(NestedProjects) = preSolution
{45EC1473-C678-4857-A544-07DFE0D0B478} = {04929013-5BAB-42B0-B9B2-8F2BB8F16AF2}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {5F3F555C-855F-4BE8-B526-D062D3E8ACA4}
EndGlobalSection
EndGlobal

View File

@ -1,7 +1,7 @@
![img](https://ci.appveyor.com/api/projects/status/gmu6b3ltc80hr3k9?svg=true)
[![Discord](https://discordapp.com/api/guilds/117523346618318850/widget.png)](https://discord.gg/nadekobot)
[![Documentation Status](https://readthedocs.org/projects/nadekobot/badge/?version=latest)](http://nadekobot.readthedocs.io/en/latest/?badge=latest)
[![nadeko0](https://cdn.discordapp.com/attachments/266240393639755778/281920716809699328/part1.png)](http://nadekobot.xyz)
[![nadeko0](https://cdn.discordapp.com/attachments/266240393639755778/281920716809699328/part1.png)](https://nadekobot.me)
[![nadeko1](https://cdn.discordapp.com/attachments/266240393639755778/281920134967328768/part2.png)](https://discordapp.com/oauth2/authorize?client_id=170254782546575360&scope=bot&permissions=66186303)
[![nadeko2](https://cdn.discordapp.com/attachments/266240393639755778/281920161311883264/part3.png)](http://nadekobot.readthedocs.io/en/latest/Commands%20List/)

View File

@ -3,7 +3,6 @@ You can support the project on patreon: <https://patreon.com/nadekobot> or paypa
##Table of contents
- [Help](#help)
- [Administration](#administration)
- [ClashOfClans](#clashofclans)
- [CustomReactions](#customreactions)
- [Gambling](#gambling)
- [Games](#games)
@ -13,6 +12,7 @@ You can support the project on patreon: <https://patreon.com/nadekobot> or paypa
- [Pokemon](#pokemon)
- [Searches](#searches)
- [Utility](#utility)
- [Xp](#xp)
### Administration
@ -34,7 +34,6 @@ Commands and aliases | Description | Usage
`.creatxtchanl` `.ctch` | Creates a new text channel with a given name. **Requires ManageChannels server permission.** | `.ctch TextChannelName`
`.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` `.clear` | `.prune` removes all Nadeko's messages in the last 100 messages. `.prune X` removes last `X` number of 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` number of 'Someone's' messages in the channel. | `.prune` or `.prune 5` or `.prune @Someone` or `.prune @Someone X`
`.mentionrole` `.menro` | Mentions every person from the provided role or roles (separated by a ',') on this server. **Requires MentionEveryone server permission.** | `.menro RoleName`
`.donators` | List of the lovely people who donated to keep this project alive. | `.donators`
`.donadd` | Add a donator to the database. **Bot owner only** | `.donadd Donate Amount`
@ -65,6 +64,7 @@ Commands and aliases | Description | Usage
`.antispam` | Stops people from repeating same message X times in a row. You can specify to either mute, kick or ban the offenders. Max message count is 10. **Requires Administrator server permission.** | `.antispam 3 Mute` or `.antispam 4 Kick` or `.antispam 6 Ban`
`.antispamignore` | Toggles whether antispam ignores current channel. Antispam must be enabled. | `.antispamignore`
`.antilist` `.antilst` | Shows currently enabled protection features. | `.antilist`
`.prune` `.clear` | `.prune` removes all Nadeko's messages in the last 100 messages. `.prune X` removes last `X` number of 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` number of 'Someone's' messages in the channel. | `.prune` or `.prune 5` or `.prune @Someone` or `.prune @Someone X`
`.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`
`.slowmodewl` | Ignores a role or a user from the slowmode feature. **Requires ManageMessages server permission.** | `.slowmodewl SomeRole` or `.slowmodewl AdminDude`
`.adsarm` | Toggles the automatic deletion of confirmations for `.iam` and `.iamn` commands. **Requires ManageMessages server permission.** | `.adsarm`
@ -81,7 +81,6 @@ Commands and aliases | Description | Usage
`.scclr` | Removes all startup commands. **Bot owner only** | `.scclr`
`.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 file **Bot owner only** | `.fwtoall`
`.connectshard` | Try (re)connecting a shard with a certain shardid when it dies. No one knows will it work. Keep an eye on the console for errors. **Bot owner only** | `.connectshard 2`
`.leave` | Makes Nadeko leave the server. Either server name or server ID is 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`
@ -91,7 +90,6 @@ Commands and aliases | Description | Usage
`.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 prefix the channel id with `c:` and the 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' default channel that bot is connected to. **Bot owner only** | `.announce Useless spam`
`.reloadimages` | Reloads images bot is using. Safe to use even when bot is being used heavily. **Bot owner only** | `.reloadimages`
`.greetdel` `.grdel` | Sets the time it takes (in seconds) for greet messages to be auto-deleted. Set it to 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`
@ -105,6 +103,7 @@ Commands and aliases | Description | Usage
`.timezone` | Sets this guilds timezone. This affects bot's time output in this server (logs, etc..) | `.timezone` or `.timezone GMT Standard Time`
`.warn` | Warns a user. **Requires BanMembers server permission.** | `.warn @b1nzy Very rude person`
`.warnlog` | See a list of warnings of a certain user. **Requires BanMembers server permission.** | `.warnlog @b1nzy`
`.warnlogall` | See a list of all warnings on the server. 15 users per page. **Requires BanMembers server permission.** | `.warnlogall` or `.warnlogall 2`
`.warnclear` `.warnc` | Clears all warnings from a certain user. **Requires BanMembers server permission.** | `.warnclear @PoorDude`
`.warnpunish` `.warnp` | Sets a punishment for a certain number of warnings. Provide no punishment to remove. **Requires BanMembers server permission.** | `.warnpunish 5 Ban` or `.warnpunish 3`
`.warnpunishlist` `.warnpl` | Lists punishments for warnings. | `.warnpunishlist`
@ -119,21 +118,6 @@ Commands and aliases | Description | Usage
###### [Back to ToC](#table-of-contents)
### ClashOfClans
Commands and aliases | Description | Usage
----------------|--------------|-------
`.createwar` `.cw` | Creates a new war by specifying a size (>10 and multiple of 5) and enemy clan name. **Requires ManageMessages server permission.** | `.cw 15 The Enemy Clan`
`.startwar` `.sw` | Starts a war with a given number. | `.sw 15`
`.listwar` `.lw` | Shows the active war claims by a number. Shows all wars in a short way if no number is specified. | `.lw [war_number]` or `.lw`
`.basecall` | Claims a certain base from a certain war. You can supply a name in the third optional argument to claim in someone else's place. | `.basecall [war_number] [base_number] [optional_other_name]`
`.callfinish1` `.cf1` | Finish your claim with 1 star if you destroyed a base. First argument is the war number, optional second argument is a base number if you want to finish for someone else. | `.cf1 1` or `.cf1 1 5`
`.callfinish2` `.cf2` | Finish your claim with 2 stars if you destroyed a base. First argument is the war number, optional second argument is a base number if you want to finish for someone else. | `.cf2 1` or `.cf2 1 5`
`.callfinish` `.cf` | Finish your claim with 3 stars if you destroyed a base. First argument is the war number, optional second argument is a base number if you want to finish for someone else. | `.cf 1` or `.cf 1 5`
`.endwar` `.ew` | Ends the war with a given index. | `.ew [war_number]`
`.uncall` | Removes your claim from a certain war. Optional second argument denotes a person in whose place to unclaim | `.uc [war_number] [optional_other_name]`
###### [Back to ToC](#table-of-contents)
### CustomReactions
Commands and aliases | Description | Usage
----------------|--------------|-------
@ -142,6 +126,7 @@ Commands 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 privileges and removes server custom reaction. | `.dcr 5`
`.crca` | Toggles whether the custom reaction will trigger if the triggering message contains the keyword (instead of only starting with it). | `.crca 44`
`.crdm` | Toggles whether the response message of the custom reaction will be sent as a direct message. | `.crdm 44`
`.crad` | Toggles whether the message triggering the custom reaction will be automatically deleted. | `.crad 59`
`.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`
@ -183,27 +168,31 @@ Commands and aliases | Description | Usage
`.affinity` | Sets your affinity towards someone you want to be claimed by. Setting affinity will reduce their `.claim` on you by 20%. You can leave second argument empty to clear your affinity. 30 minutes cooldown. | `.affinity @MyHusband` or `.affinity`
`.waifus` `.waifulb` | Shows top 9 waifus. You can specify another page to show other waifus. | `.waifus` or `.waifulb 3`
`.waifuinfo` `.waifustats` | Shows waifu stats for a target person. Defaults to you if no user is provided. | `.waifuinfo @MyCrush` or `.waifuinfo`
`.waifugift` `.gift` `.gifts` | Gift an item to someone. This will increase their waifu value by 50% of the gifted item's value if they don't have affinity set towards you, or 100% if they do. Provide no arguments to see a list of items that you can gift. | `.gifts` or `.gift Rose @Himesama`
`.wheeloffortune` `.wheel` | Bets a certain amount of currency on the wheel of fortune. Wheel can stop on one of many different multipliers. Won amount is rounded down to the nearest whole number. | `.wheel 10`
###### [Back to ToC](#table-of-contents)
### Games
Commands and aliases | Description | Usage
----------------|--------------|-------
`.leet` | Converts a text to leetspeak with 6 (1-6) severity levels | `.leet 3 Hello`
`.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`
`.rategirl` | Use the universal hot-crazy wife zone matrix to determine the girl's worth. It is everything young men need to know about women. At any moment in time, any woman you have previously located on this chart can vanish from that location and appear anywhere else on the chart. | `.rategirl @SomeGurl`
`.linux` | Prints a customizable Linux interjection | `.linux Spyware Windows`
`.leet` | Converts a text to leetspeak with 6 (1-6) severity levels | `.leet 3 Hello`
`.acrophobia` `.acro` | Starts an Acrophobia game. Second argument is optional round length in seconds. (default is 60) | `.acro` or `.acro 30`
`.cleverbot` | Toggles cleverbot session. When enabled, the bot will reply to messages starting with bot mention in the server. Custom reactions starting with %mention% won't work if cleverbot is enabled. **Requires ManageMessages server permission.** | `.cleverbot`
`.connect4` `.con4` | Creates or joins an existing connect4 game. 2 players are required for the game. Objective of the game is to get 4 of your pieces next to each other in a vertical, horizontal or diagonal line. | `.connect4`
`.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`
`.hangmanstop` | Stops the active hangman game on this channel if it exists. | `.hangmanstop`
`.nunchi` | Creates or joins an existing nunchi game. Users have to count up by 1 from the starting number shown by the bot. If someone makes a mistake (types an incorrent number, or repeats the same number) they are out of the game and a new round starts without them. Minimum 3 users required. | `.nunchi`
`.pick` | Picks the currency planted in this channel. 60 seconds cooldown. | `.pick`
`.plant` | Spend an amount of currency to plant it in this channel. Default is 1. (If bot is restarted or crashes, the currency will be lost) | `.plant` or `.plant 5`
`.gencurrency` `.gc` | Toggles currency generation on this channel. Every posted message will have chance to spawn currency. Chance is specified by the Bot Owner. (default is 2%) **Requires ManageMessages server permission.** | `.gc`
`.poll` | Creates a poll which requires users to send the number of the voting option to the bot. **Requires ManageMessages server permission.** | `.poll Question?;Answer1;Answ 2;A_3`
`.publicpoll` `.ppoll` | Creates a public poll which requires users to type a number of the voting option in the channel command is ran in. **Requires ManageMessages server permission.** | `.ppoll Question?;Answer1;Answ 2;A_3`
`.poll` `.ppoll` | Creates a public poll which requires users to type a number of the voting option in the channel command is ran in. **Requires ManageMessages server permission.** | `.ppoll Question?;Answer1;Answ 2;A_3`
`.pollstats` | Shows the poll results without stopping the poll on this server. **Requires ManageMessages server permission.** | `.pollstats`
`.pollend` | Stops active poll on this server and prints the results in this channel. **Requires ManageMessages server permission.** | `.pollend`
`.typestart` | Starts a typing contest. | `.typestart`
@ -211,7 +200,7 @@ Commands and aliases | Description | Usage
`.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`
`.tictactoe` `.ttt` | Starts a game of tic tac toe. Another user must run the command in the same channel in order to accept the challenge. Use numbers 1-9 to play. 15 seconds per move. | >ttt
`.tictactoe` `.ttt` | Starts a game of tic tac toe. Another user must run the command in the same channel in order to accept the challenge. Use numbers 1-9 to play. 15 seconds per move. | .ttt
`.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`
@ -233,35 +222,38 @@ Commands and aliases | Description | Usage
### Music
Commands and aliases | Description | Usage
----------------|--------------|-------
`.play` `.start` | If no arguments are specified, acts as `.next 1` command. If you specify a song number, it will jump to that song. If you specify a search query, acts as a `.q` command | `.play` or `.play 5` or `.play Dream Of Venice`
`.queue` `.q` `.yq` | Queue a song using keywords or a link. Bot will join your voice channel. **You must be in a voice channel**. | `.q Dream Of Venice`
`.queuenext` `.qn` | Works the same as `.queue` command, except it enqueues the new song after the current one. **You must be in a voice channel**. | `.qn Dream Of Venice`
`.queuesearch` `.qs` `.yqs` | Search for top 5 youtube song result using keywords, and type the index of the song to play that song. Bot will join your voice channel. **You must be in a voice channel**. | `.qs Dream Of Venice`
`.listqueue` `.lq` | Lists 10 currently queued songs per page. Default page is 1. | `.lq` or `.lq 2`
`.next` `.n` | Goes to the next song in the queue. You have to be in the same voice channel as the bot. You can skip multiple songs, but in that case songs will not be requeued if .rcs or .rpl is enabled. | `.n` or `.n 5`
`.stop` `.s` | Stops the music and clears the playlist. Stays in the channel. | `.s`
`.stop` `.s` | Stops the music and preserves the current song index. 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, the bot 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`
`.queuesearch` `.qs` `.yqs` | Search for top 5 youtube song result using keywords, and type the index of the song to play that song. Bot will join your voice channel. **You must be in a voice channel**. | `.qs 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`
`.nowplaying` `.np` | Shows the song that the bot is currently playing. | `.np`
`.volume` `.vol` | Sets the music playback volume (0-100%) | `.vol 50`
`.defvol` `.dv` | Sets the default music volume when music playback is started (0-100). Persists through restarts. | `.dv 80`
`.playlistshuffle` `.plsh` | Shuffles the current playlist. | `.plsh`
`.playlist` `.pl` | Queues up to 500 songs from a youtube playlist specified by a link, or keywords. | `.pl playlist link or name`
`.songremove` `.srm` | Remove a song by its # in the queue, or 'all' to remove all songs from the queue and reset the song index. | `.srm 5`
`.playlists` `.pls` | Lists all playlists. Paginated, 20 per page. Default page is 0. | `.pls 1`
`.deleteplaylist` `.delpls` | Deletes a saved playlist. Works only if you made it or if you are the bot owner. | `.delpls animu-5`
`.save` | Saves a playlist under a certain name. Playlist name must be no longer than 20 characters and must not contain dashes. | `.save classical1`
`.load` | Loads a saved playlist using its ID. Use `.pls` to list all saved playlists and `.save` to save new ones. | `.load 5`
`.fairplay` `.fp` | Toggles fairplay. While enabled, the bot will prioritize songs from users who didn't have their song recently played instead of the song's position in the queue. | `.fp`
`.songautodelete` `.sad` | Toggles whether the song should be automatically removed from the music queue when it finishes playing. | `.sad`
`.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`
`.soundcloudpl` `.scpl` | Queue a Soundcloud playlist using a link. | `.scpl soundcloudseturl`
`.localplaylst` `.lopl` | Queues all songs from a directory. **Bot owner only** | `.lopl C:/music/classical`
`.nowplaying` `.np` | Shows the song that the bot is currently playing. | `.np`
`.shuffle` `.sh` `.plsh` | Shuffles the current playlist. | `.plsh`
`.playlist` `.pl` | Queues up to 500 songs from a youtube playlist specified by a link, or keywords. | `.pl playlist link or name`
`.radio` `.ra` | Queues a radio stream from a link. It can be a direct mp3 radio stream, .m3u, .pls .asx or .xspf (Usage Video: <https://streamable.com/al54>) | `.ra radio link here`
`.local` `.lo` | Queues a local file by specifying a full path. **Bot owner only** | `.lo C:/music/mysong.mp3`
`.songremove` `.srm` | Remove a song by its # in the queue, or 'all' to remove all songs from the queue. | `.srm 5`
`.localplaylst` `.lopl` | Queues all songs from a directory. **Bot owner only** | `.lopl C:/music/classical`
`.move` `.mv` | Moves the bot to your voice channel. (works only if music is already playing) | `.mv`
`.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. Playlist name must be no longer than 20 characters and must not contain dashes. | `.save classical1`
`.load` | Loads a saved playlist using its 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. Works only if you made it or if you are the bot owner. | `.delpls animu-5`
`.goto` | Goes to a specific time in seconds in a song. | `.goto 30`
`.autoplay` `.ap` | Toggles autoplay - When the song is finished, automatically queue a related Youtube song. (Works only for Youtube songs and when queue is empty) | `.ap`
`.setmusicchannel` `.smch` | Sets the current channel as the default music output channel. This will output playing, finished, paused and removed songs to that channel instead of the channel where the first song was queued in. **Requires ManageMessages server permission.** | `.smch`
@ -281,6 +273,8 @@ Commands and aliases | Description | Usage
`.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`
`.boobs` | Real adult content. | `.boobs`
`.butts` `.ass` `.butt` | Real adult content. | `.butts` or `.ass`
`.nsfwtagbl` `.nsfwtbl` | Toggles whether the tag is blacklisted or not in nsfw searches. Provide no parameters to see the list of blacklisted tags. | `.nsfwtbl poop`
`.nsfwcc` | Clears nsfw cache. **Bot owner only** | `.nsfwcc`
###### [Back to ToC](#table-of-contents)
@ -382,11 +376,11 @@ Commands and aliases | Description | Usage
`.place` | Shows a placeholder image of a given tag. Use `.placelist` to see all available tags. You can specify the width and height of the image as the last two optional arguments. | `.place Cage` or `.place steven 500 400`
`.pokemon` `.poke` | Searches for a pokemon. | `.poke Sylveon`
`.pokemonability` `.pokeab` | Searches for a pokemon ability. | `.pokeab overgrow`
`.hitbox` `.hb` | Notifies this channel when a certain user starts streaming. **Requires ManageMessages server permission.** | `.hitbox SomeStreamer`
`.smashcast` `.hb` | Notifies this channel when a certain user starts streaming. **Requires ManageMessages server permission.** | `.smashcast 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`
`.mixer` `.bm` | Notifies this channel when a certain user starts streaming. **Requires ManageMessages server permission.** | `.mixer SomeStreamer`
`.liststreams` `.ls` | Lists all streams you are following on this server. | `.ls`
`.removestream` `.rms` | Removes notifications of a certain streamer from a certain platform on this channel. **Requires ManageMessages server permission.** | `.rms Twitch SomeGuy` or `.rms Beam SomeOtherGuy`
`.removestream` `.rms` | Removes notifications of a certain streamer from a certain platform on this channel. **Requires ManageMessages server permission.** | `.rms Twitch SomeGuy` or `.rms mixer 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`
@ -399,7 +393,6 @@ Commands and aliases | Description | Usage
### Utility
Commands and aliases | Description | Usage
----------------|--------------|-------
`.rotaterolecolor` `.rrc` | Rotates a roles color on an interval with a list of supplied colors. First argument is interval in seconds (Minimum 60). Second argument is a role, followed by a space-separated list of colors in hex. Provide a rolename with a 0 interval to disable. **Requires ManageRoles server permission.** **Bot owner only** | `.rrc 60 MyLsdRole #ff0000 #00ff00 #0000ff` or `.rrc 0 MyLsdRole`
`.togethertube` `.totube` | Creates a new room on <https://togethertube.com> and shows the link in the chat. | `.totube`
`.whosplaying` `.whpl` | Shows a list of users who are playing the specified game. | `.whpl Overwatch`
`.inrole` | Lists every person from the specified role on this server. You can use role ID, role name. | `.inrole Some Role`
@ -411,20 +404,17 @@ Commands and aliases | Description | Usage
`.channeltopic` `.ct` | Sends current channel's topic as a message. | `.ct`
`.createinvite` `.crinv` | Creates a new invite which has infinite max uses and never expires. **Requires CreateInstantInvite channel permission.** | `.crinv`
`.shardstats` | Stats for shards. Paginated with 25 shards per page. | `.shardstats` or `.shardstats 2`
`.shardid` | Shows which shard is a certain guild on, by guildid. | `.shardid 117523346618318850`
`.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`
`.savechat` | Saves a number of messages to a text file and sends it to you. **Bot owner only** | `.savechat 150`
`.ping` | Ping the bot to see if there are latency issues. | `.ping`
`.botconfigedit` `.bce` | Sets one of available bot config settings to a specified value. Use the command without any parameters to get a list of available settings. **Bot owner only** | `.bce CurrencyName b1nzy` or `.bce`
`.calculate` `.calc` | Evaluate a mathematical expression. | `.calc 1+1`
`.calcops` | Shows all available operations in the `.calc` command | `.calcops`
`.alias` `.cmdmap` | Create a custom alias for a certain Nadeko command. Provide no alias to remove the existing one. **Requires Administrator server permission.** | `.alias allin $bf 100 h` or `.alias "linux thingy" >loonix Spyware Windows`
`.aliaslist` `.cmdmaplist` `.aliases` | Shows the list of currently set aliases. Paginated. | `.aliaslist` or `.aliaslist 3`
`.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 a cross server channel instance from this channel. **Requires ManageServer server permission.** | `.lcsc`
`.serverinfo` `.sinfo` | Shows info about the server the bot is on. If no channel is supplied, it defaults to current one. | `.sinfo Some Server`
`.serverinfo` `.sinfo` | Shows info about the server the bot is on. If no server 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`
`.activity` | Checks for spammers. **Bot owner only** | `.activity`
@ -443,6 +433,39 @@ Commands and aliases | Description | Usage
`.repeatremove` `.reprm` | Removes a repeating message on a specified index. Use `.repeatlist` to see indexes. **Requires ManageMessages server permission.** | `.reprm 2`
`.repeat` | Repeat a message every `X` minutes in the current channel. You can instead specify time of day for the message to be repeated at daily (make sure you've set your server's timezone). You can have up to 5 repeating messages on the server in total. **Requires ManageMessages server permission.** | `.repeat 5 Hello there` or `.repeat 17:30 tea time`
`.repeatlist` `.replst` | Shows currently repeating messages and their indexes. **Requires ManageMessages server permission.** | `.repeatlist`
`.streamrole` | Sets a role which is monitored for streamers (FromRole), and a role to add if a user from 'FromRole' is streaming (AddRole). When a user from 'FromRole' starts streaming, they will receive an 'AddRole'. Provide no arguments to disable **Requires ManageRoles server permission.** | `.streamrole "Eligible Streamers" "Featured Streams"`
`.streamrolekw` `.srkw` | Sets keyword which is required in the stream's title in order for the streamrole to apply. Provide no keyword in order to reset. **Requires ManageRoles server permission.** | `.srkw` or `.srkw PUBG`
`.streamrolebl` `.srbl` | Adds or removes a blacklisted user. Blacklisted users will never receive the stream role. **Requires ManageRoles server permission.** | `.srbl add @b1nzy#1234` or `.srbl rem @b1nzy#1234`
`.streamrolewl` `.srwl` | Adds or removes a whitelisted user. Whitelisted users will receive the stream role even if they don't have the specified keyword in their stream title. **Requires ManageRoles server permission.** | `.srwl add @b1nzy#1234` or `.srwl rem @b1nzy#1234`
`.convertlist` | List of the convertible dimensions and currencies. | `.convertlist`
`.convert` | Convert quantities. Use `.convertlist` to see supported dimensions and currencies. | `.convert m km 1000`
`.verboseerror` `.ve` | Toggles whether the bot should print command errors when a command is incorrectly used. **Requires ManageMessages server permission.** | `.ve`
###### [Back to ToC](#table-of-contents)
### Xp
Commands and aliases | Description | Usage
----------------|--------------|-------
`.experience` `.xp` | Shows your xp stats. Specify the user to show that user's stats instead. | `.xp`
`.xprolerewards` `.xprrs` | Shows currently set role rewards. | `.xprrs`
`.xprolereward` `.xprr` | Sets a role reward on a specified level. Provide no role name in order to remove the role reward. **Requires ManageRoles server permission.** | `.xprr 3 Social`
`.xpnotify` `.xpn` | Sets how the bot should notify you when you get a `server` or `global` level. You can set `dm` (for the bot to send a direct message), `channel` (to get notified in the channel you sent the last message in) or `none` to disable. | `.xpn global dm` `.xpn server channel`
`.xpexclude` `.xpex` | Exclude a user or a role from the xp system, or whole current server. **Requires Administrator server permission.** | `.xpex Role Excluded-Role` `.xpex Server`
`.xpexclusionlist` `.xpexl` | Shows the roles and channels excluded from the XP system on this server, as well as whether the whole server is excluded. | `.xpexl`
`.xpleaderboard` `.xplb` | Shows current server's xp leaderboard. | `.xplb`
`.xpgleaderboard` `.xpglb` | Shows the global xp leaderboard. | `.xpglb`
`.xpadd` | Adds xp to a user on the server. This does not affect their global ranking. You can use negative values. **Requires Administrator server permission.** | `.xpadd 100 @b1nzy`
`.clubcreate` | Creates a club. You must be atleast level 5 and not be in the club already. | `.clubcreate b1nzy's friends`
`.clubicon` | Sets the club icon. | `.clubicon https://i.imgur.com/htfDMfU.png`
`.clubinfo` | Shows information about the club. | `.clubinfo b1nzy's friends#123`
`.clubbans` | Shows the list of users who have banned from your club. Paginated. You must be club owner to use this command. | `.clubbans 2`
`.clubapps` | Shows the list of users who have applied to your club. Paginated. You must be club owner to use this command. | `.clubapps 2`
`.clubapply` | Apply to join a club. You must meet that club's minimum level requirement, and not be on its ban list. | `.clubapply b1nzy's friends#123`
`.clubaccept` | Accept a user who applied to your club. | `.clubaccept b1nzy#1337`
`.clubleave` | Leaves the club you're currently in. | `.clubleave`
`.clubkick` | Kicks the user from the club. You must be the club owner. They will be able to apply again. | `.clubkick b1nzy#1337`
`.clubban` | Bans the user from the club. You must be the club owner. They will not be able to apply again. | `.clubban b1nzy#1337`
`.clubunban` | Unbans the previously banned user from the club. You must be the club owner. | `.clubunban b1nzy#1337`
`.clublevelreq` | Sets the club required level to apply to join the club. You must be club owner. You can't set this number below 5. | `.clublevelreq 7`
`.clubdisband` | Disbands the club you're the owner of. This action is irreversible. | `.clubdisband`
`.clublb` | Shows club rankings on the specified page. | `.clublb 2`

View File

@ -1,6 +1,6 @@
### How to contribute
1. Make Pull Requests to the [**dev BRANCH**](https://github.com/Kwoth/NadekoBot/tree/dev).
1. Make Pull Requests to the [**1.9 BRANCH**](https://github.com/Kwoth/NadekoBot/tree/1.9).
2. Keep 1 Pull Request to a single feature.
3. Explain what you did in the PR message.

View File

@ -36,14 +36,4 @@ For example:
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!"|
|`%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. 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
To learn about placeholders, go [here](Placeholders.md)

View File

@ -16,9 +16,14 @@ If you do not see `credentials.json` you will need to rename `credentials_exampl
"GoogleApiKey": "AIzaSyDSci1sdlWQOWNVj1vlXxxxxxbk0oWMEzM",
"MashapeKey": "4UrKpcWXc2mshS8RKi00000y8Kf5p1Q8kI6jsn32bmd8oVWiY7",
"OsuApiKey": "4c8c8fdff8e1234581725db27fd140a7d93320d6",
"CleverbotApiKey": "",
"PatreonAccessToken": "",
"PatreonCampaignId": "334038",
"Db": null,
"TotalShards": 1
"TotalShards": 1,
"ShardRunCommand": "",
"ShardRunArguments": "",
"ShardRunPort": null
}
```
-----
@ -43,7 +48,7 @@ If you do not see `credentials.json` you will need to rename `credentials_exampl
- Replace the **`12345678`** in this link:
`https://discordapp.com/oauth2/authorize?client_id=`**`12345678`**`&scope=bot&permissions=66186303` with your `Client ID`.
- The link should now look like this:
`https://discordapp.com/oauth2/authorize?client_id=`**`YOUR_CLENT_ID_HERE`**`&scope=bot&permissions=66186303`
`https://discordapp.com/oauth2/authorize?client_id=`**`YOUR_CLIENT_ID_HERE`**`&scope=bot&permissions=66186303`
- Go to the newly created link and pick the server we created, and click `Authorize`.
- The bot should have been added to your server.
@ -144,25 +149,30 @@ It should look like:
- Required for Urban Disctionary, Hashtag search, and Hearthstone cards.
- You need to create an account on their [api marketplace](https://market.mashape.com/), after that go to `market.mashape.com/YOURNAMEHERE/applications/default-application` and press **Get the keys** in the top right corner.
- Copy the key and paste it into `credentials.json`
- **LOLAPIKey**
- **LoLApiKey**
- Required for all League of Legends commands.
- You can get this key [here](http://api.champion.gg/)
- **OsuAPIKey**
- You can get this key [here.](http://api.champion.gg/)
- **OsuApiKey**
- Required for Osu commands
- You can get this key [here.](https://osu.ppy.sh/p/api)
- You can get this key [here.](https://osu.ppy.sh/p/api)
- **CleverbotApiKey**
- Required if you want to use official cleverobot, instead of program-o
- you can get this key [here.](http://www.cleverbot.com/api/)
- **PatreonAccessToken**
- For Patreon creators only.
- **PatreonCampaignId**
- For Patreon creators only. Id of your campaign.
- **TotalShards**
- Required if the bot will be connected to more than 1500 servers.
- Most likely unnecessary to change until your bot is added to more than 1500 servers.
- Most likely unnecessary to change until your bot is added to more than 1500 servers.
-----
## DB files
Nadeko saves all the settings and infomations in `NadekoBot.db` file here:
`NadekoBot/src/NadekoBot/bin/Release/netcoreapp1.1/data/NadekoBot.db` (macOS and Linux)
`NadekoBot\system\data` (Windows)
`NadekoBot\system\data` (Windows)
in order to open the database file you will need [DB Browser for SQLite](http://sqlitebrowser.org/).
*NOTE: You don't have to worry if you don't have `NadekoBot.db` file, it gets auto created once you run the bot successfully.*
@ -179,11 +189,29 @@ in order to open the database file you will need [DB Browser for SQLite](http://
- click on **Apply**
- click on **Write Changes**
![nadekodb](https://cdn.discordapp.com/attachments/251504306010849280/254067055240806400/nadekodb.gif)
and that will save all the changes.
## Sharding your bot
- **ShardRunCommand**
- Command with which to run shards 1+
- Required if you're sharding your bot on windows using .exe, or in a custom way.
- This internally defaults to `dotnet`
- For example, if you want to shard your NadekoBot which you installed using windows installer, you would want to set it to something like this: `C:\Program Files\NadekoBot\system\NadekoBot.exe`
- **ShardRunArguments**
- Arguments to the shard run command
- Required if you're sharding your bot on windows using .exe, or in a custom way.
- This internally defaults to `run -c Release -- {0} {1} {2}` which will be enough to run linux and other 'from source' setups
- {0} will be replaced by the `shard ID` of the shard being ran, {1} by the shard 0's process id, and {2} by the port shard communication is happening on
- If shard0 (main window) is closed, all other shards will close too
- For example, if you want to shard your NadekoBot which you installed using windows installer, you would want to set it to `{0} {1} {2}`
- **ShardRunPort**
- Bot uses a random UDP port in [5000, 6000) range for communication between shards
![nadekodb](https://cdn.discordapp.com/attachments/251504306010849280/254067055240806400/nadekodb.gif)
[Google Console]: https://console.developers.google.com
[DiscordApp]: https://discordapp.com/developers/applications/me
[Invite Guide]: http://discord.kongslien.net/guide.html
[Invite Guide]: http://discord.kongslien.net/guide.html

View File

@ -49,11 +49,11 @@ To allow users to only see the current song and have a DJ role for queuing follo
* Disables music commands for everybody
2. `.sc !!nowplaying enable`
2. `.sc .nowplaying enable`
* Enables the "nowplaying" command for everyone
3. `.sc !!listqueue enable`
3. `.sc .listqueue enable`
* Enables the "listqueue" command for everyone

25
docs/Placeholders.md Normal file
View File

@ -0,0 +1,25 @@
Placeholders are used in Quotes, Custom Reactions, Greet/bye messages, playing statuses, and a few other places.
They can be used to make the message more user friendly, generate random numbers or pictures, etc...
Some features have their own specific placeholders which are noted in that feature's command help. Some placeholders are not available in certain features because they don't make sense there.
### Here is a list of the usual placeholders:
- `%mention%` - Mention the bot
- `%shardid%` - Shard id
- `%server%` - Server name
- `%sid%` - Server Id
- `%channel%` - Channel mention
- `%chname%` - Channel mention
- `%cid%` - Channel Id
- `%user%` - User mention
- `%id%` or `%uid%` - User Id
- `%userfull%` - Username#discriminator
- `%userdiscrim%` - discriminator (for example 1234)
- `%rngX-Y%` - Replace X and Y with the range (for example `%rng5-10%` - random between 5 and 10)
- `%time%` - Bot time
- `%server_time%` - Time on this server, set with `.timezone` command
**If you're using placeholders in embeds, don't use %user% and in titles, footers and field names. They will not show properly.**
![img](http://i.imgur.com/lNNNfs1.png)

View File

@ -1,5 +1,5 @@
# NadekoBot a Discord bot
Nadeko is written in C# and Discord.net for more information visit <https://github.com/Kwoth/NadekoBot>
Nadeko is written in C# and Discord.Net for more information visit <https://github.com/Kwoth/NadekoBot>
## Install Docker
Follow the respective guide for your operating system found here [Docker Engine Install Guide](https://docs.docker.com/engine/installation/)
@ -8,20 +8,18 @@ Follow the respective guide for your operating system found here [Docker Engine
For this guide we will be using the folder /nadeko as our config root folder.
```bash
docker create --name=nadeko -v /nadeko/:/root/nadeko uirel/nadeko:1.4
docker create --name=nadeko -v /nadeko/conf/:/root/nadeko -v /nadeko/data:/opt/NadekoBot/src/NadekoBot/bin/Release/netcoreapp1.1/data uirel/nadeko:1.4
```
-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 coming from a previous version of nadeko (the old docker) make sure your credentials.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 place it in the /nadeko folder [Nadeko JSON Guide](http://nadekobot.readthedocs.io/en/latest/JSON%20Explanations/)
Next start the docker up with
`docker start nadeko; docker logs -f nadeko`
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
After a few moments you should be able to invite Nadeko to your server. If you cannot, check the log file for errors.
## Monitoring
@ -37,22 +35,24 @@ The following commands are required for the default options
`docker stop nadeko; docker rm nadeko`
`docker create --name=nadeko -v /nadeko/:/root/nadeko uirel/nadeko:1.4`
```
docker create --name=nadeko -v /nadeko/conf/:/root/nadeko -v /nadeko/data:/opt/NadekoBot/src/NadekoBot/bin/Release/netcoreapp1.1/data uirel/nadeko:1.4
```
`docker start nadeko`
# Automatic Updates
Automatic update are now handled by watchertower [WatchTower GitHub](https://github.com/CenturyLinkLabs/watchtower)
To setup watchtower to keep Nadeko up-to-date for you with the default settings use the following command
Automatic update are now handled by WatchTower [WatchTower GitHub](https://github.com/CenturyLinkLabs/watchtower)
To setup WatchTower to keep Nadeko up-to-date for you with the default settings, use the following command
```bash
docker run -d --name watchtower -v /var/run/docker.sock:/var/run/docker.sock centurylink/watchtower --cleanup nadeko
```
This will check for updates to the docker every 5 minutes and update immediately. Alternatively using the `--interval X` command to change the interval, where X is the amount of time in seconds to wait. eg 21600 for 6 hours.
This will check for updates to the docker every 5 minutes and update immediately. Alternatively using the `--interval X` command to change the interval, where X is the amount of time in seconds to wait. e.g 21600 for 6 hours.
If you have any issues with the docker setup, please ask in #help but indicate you are using the docker.
If you have any issues with the docker setup, please ask in #help channel on our [Discord server](https://discordapp.com/invite/nadekobot) 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.
For information about configuring your bot or its functionality, please check the [documentation](http://nadekobot.readthedocs.io/en/latest).

View File

@ -24,6 +24,8 @@ brew install opusfile
brew install libffi
brew install libsodium
brew install tmux
brew install python
brew install youtube-dl
```
#### Installing .NET Core SDK

View File

@ -1,9 +1,9 @@
#### If you have NadekoBot 1.x on Windows
- Go to `NadekoBot\src\NadekoBot` and backup your `credentials.json` file; then go to `NadekoBot\src\NadekoBot\bin\Release\netcoreapp1.0` and backup your `data` folder.
- Follow the [Windows Guide](http://nadekobot.readthedocs.io/en/latest/guides/Windows%20Guide/) and install the latest version of **NadekoBot**.
- Navigate to your **old** `Nadeko` folder and copy your `credentials.json` file and the `data` folder.
- Paste credentials into the **NadekoBot 1.4x+** `C:\Program Files\NadekoBot\system` folder.
- Paste your **old** `Nadeko` data folder into **NadekoBot 1.4x+** `C:\Program Files\NadekoBot\system` folder.
- Paste your `credentials.json` file into the `C:\Program Files\NadekoBot\system` folder.
- Paste your `data` folder into `C:\Program Files\NadekoBot\system` folder.
- If it asks you to overwrite files, it is fine to do so.
- Next launch your **new** Nadeko as the guide describes, if it is not already running.
@ -27,4 +27,4 @@
- **For Ubuntu, Debian and CentOS Users Only:** use the option `4. Auto-Install Prerequisites` to install the latest version of .NET Core SDK.
- Use option `1. Download NadekoBot` to update your NadekoBot to 1.4.x.
- Next, just [run your NadekoBot.](http://nadekobot.readthedocs.io/en/latest/guides/Linux%20Guide/#running-nadekobot)
- *NOTE: 1.4.x uses `NadekoBot.db` file from `NadekoBot/src/NadekoBot/bin/Release/netcoreapp1.1/data` folder.*
- *NOTE: 1.4.x uses `NadekoBot.db` file from `NadekoBot/src/NadekoBot/bin/Release/netcoreapp1.1/data` folder.*

View File

@ -3,12 +3,11 @@
#### Prerequisites
- [Notepad++][Notepad++] (or some other decent text editor)
- Windows 8 or later
- [.NET Core SDK (Command line / other)][.NET Core SDK]
- [Create Discord Bot application](http://nadekobot.readthedocs.io/en/latest/JSON%20Explanations/#creating-discord-bot-application) and [Invite the bot to your server](http://nadekobot.readthedocs.io/en/latest/JSON%20Explanations/#inviting-your-bot-to-your-server).
#### Guide
- Download and run the [NadekoBot Updater.][Updater]
- Press **`Install ffmpeg`** button if you want music features.
- Press **`Install ffmpeg`** and **`Install youtube-dl`** if you want music features.
***NOTE:** RESTART YOUR PC IF YOU DO.*
- Press **`Update`** and go through the installation wizard.
***NOTE:** If you're upgrading from 1.3, DO NOT select your old nadekobot folder. Install it in a separate directory and read the [upgrading guide](http://nadekobot.readthedocs.io/en/latest/guides/Upgrading%20Guide/).*

View File

@ -28,6 +28,7 @@ If you want to contribute, be sure to PR on the **[1.4][1.4]** branch.
- [Permissions System](Permissions System.md)
- [JSON Explanations](JSON Explanations.md)
- [Custom Reactions](Custom Reactions.md)
- [Placeholders](Placeholders.md)
- [Frequently Asked Questions](Frequently Asked Questions.md)
- [Contribution Guide](Contribution Guide.md)
- [Donate](Donate.md)

View File

@ -1,3 +1,3 @@
{
"sdk": { "version": "1.0.1" }
"sdk": { "version": "2.0.0" }
}

View File

@ -14,9 +14,10 @@ pages:
- Readme: Readme.md
- Commands List: Commands List.md
- Features Explained:
- Permissions System: Permissions System.md
- JSON Explanations: JSON Explanations.md
- Permissions System: Permissions System.md
- Custom Reactions: Custom Reactions.md
- Placeholders: Placeholders.md
- Frequently Asked Questions: Frequently Asked Questions.md
- Contribution Guide: Contribution Guide.md
- ❤ Donate ❤: Donate.md

View File

@ -1,14 +0,0 @@
using Discord.Commands;
using NadekoBot.Services;
using System.Linq;
using System.Runtime.CompilerServices;
namespace NadekoBot.Attributes
{
public class Aliases : AliasAttribute
{
public Aliases([CallerMemberName] string memberName = "") : base(Localization.LoadCommandString(memberName.ToLowerInvariant() + "_cmd").Split(' ').Skip(1).ToArray())
{
}
}
}

View File

@ -1,14 +0,0 @@
using Discord.Commands;
using NadekoBot.Services;
using System.Runtime.CompilerServices;
namespace NadekoBot.Attributes
{
public class Description : SummaryAttribute
{
public Description([CallerMemberName] string memberName="") : base(Localization.LoadCommandString(memberName.ToLowerInvariant() + "_desc"))
{
}
}
}

View File

@ -1,14 +0,0 @@
using Discord.Commands;
using NadekoBot.Services;
using System.Runtime.CompilerServices;
namespace NadekoBot.Attributes
{
public class NadekoCommand : CommandAttribute
{
public NadekoCommand([CallerMemberName] string memberName="") : base(Localization.LoadCommandString(memberName.ToLowerInvariant() + "_cmd").Split(' ')[0])
{
}
}
}

View File

@ -1,14 +0,0 @@
using Discord.Commands;
using NadekoBot.Services;
using System.Runtime.CompilerServices;
namespace NadekoBot.Attributes
{
public class Usage : RemarksAttribute
{
public Usage([CallerMemberName] string memberName="") : base(Localization.LoadCommandString(memberName.ToLowerInvariant()+"_usage"))
{
}
}
}

View File

@ -2,7 +2,7 @@
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
namespace NadekoBot.DataStructures
namespace NadekoBot.Common
{
public class AsyncLazy<T> : Lazy<Task<T>>
{

View File

@ -0,0 +1,13 @@
using System.Linq;
using System.Runtime.CompilerServices;
using Discord.Commands;
using NadekoBot.Services.Impl;
namespace NadekoBot.Common.Attributes
{
public class Aliases : AliasAttribute
{
public Aliases([CallerMemberName] string memberName = "") : base(Localization.LoadCommand(memberName.ToLowerInvariant()).Cmd.Split(' ').Skip(1).ToArray())
{
}
}
}

View File

@ -0,0 +1,14 @@
using System.Runtime.CompilerServices;
using Discord.Commands;
using NadekoBot.Services.Impl;
namespace NadekoBot.Common.Attributes
{
public class Description : SummaryAttribute
{
public Description([CallerMemberName] string memberName="") : base(Localization.LoadCommand(memberName.ToLowerInvariant()).Desc)
{
}
}
}

View File

@ -0,0 +1,14 @@
using System.Runtime.CompilerServices;
using Discord.Commands;
using NadekoBot.Services.Impl;
namespace NadekoBot.Common.Attributes
{
public class NadekoCommand : CommandAttribute
{
public NadekoCommand([CallerMemberName] string memberName="") : base(Localization.LoadCommand(memberName.ToLowerInvariant()).Cmd.Split(' ')[0])
{
}
}
}

View File

@ -1,7 +1,7 @@
using Discord.Commands;
using System;
using System;
using Discord.Commands;
namespace NadekoBot.Attributes
namespace NadekoBot.Common.Attributes
{
[AttributeUsage(AttributeTargets.Class)]
sealed class NadekoModuleAttribute : GroupAttribute

View File

@ -1,9 +1,9 @@
using System.Threading.Tasks;
using System;
using System.Threading.Tasks;
using Discord.Commands;
using System;
using NadekoBot.Services;
namespace NadekoBot.Attributes
namespace NadekoBot.Common.Attributes
{
public class OwnerOnlyAttribute : PreconditionAttribute
{

View File

@ -0,0 +1,24 @@
using System.Runtime.CompilerServices;
using Discord.Commands;
using NadekoBot.Services.Impl;
using System.Linq;
using Discord;
namespace NadekoBot.Common.Attributes
{
public class Usage : RemarksAttribute
{
public Usage([CallerMemberName] string memberName="") : base(Usage.GetUsage(memberName))
{
}
public static string GetUsage(string memberName)
{
var usage = Localization.LoadCommand(memberName.ToLowerInvariant()).Usage;
return string.Join(" or ", usage
.Select(x => Format.Code(x)));
}
}
}

View File

@ -0,0 +1,26 @@
namespace NadekoBot.Common
{
public enum BotConfigEditType
{
BetflipMultiplier,
Betroll100Multiplier,
Betroll67Multiplier,
Betroll91Multiplier,
CurrencyGenerationChance,
CurrencyGenerationCooldown,
CurrencyName,
CurrencyPluralName,
CurrencySign,
DmHelpString,
HelpString,
CurrencyDropAmount,
CurrencyDropAmountMax,
MinimumBetAmount,
TriviaCurrencyReward,
XpPerMessage,
XpMinutesTimeout,
//ErrorColor, //after i fix the nadekobot.cs static variables
//OkColor
}
}

View File

@ -1,8 +1,10 @@
using Discord;
using System;
using Discord;
using NadekoBot.Extensions;
using Newtonsoft.Json;
using NLog;
namespace NadekoBot.DataStructures
namespace NadekoBot.Common
{
public class CREmbed
{
@ -31,19 +33,31 @@ namespace NadekoBot.DataStructures
public EmbedBuilder ToEmbed()
{
var embed = new EmbedBuilder()
.WithTitle(Title)
.WithDescription(Description)
.WithColor(new Discord.Color(Color));
var embed = new EmbedBuilder();
if (!string.IsNullOrWhiteSpace(Title))
embed.WithTitle(Title);
if (!string.IsNullOrWhiteSpace(Description))
embed.WithDescription(Description);
embed.WithColor(new Discord.Color(Color));
if (Footer != null)
embed.WithFooter(efb => efb.WithIconUrl(Footer.IconUrl).WithText(Footer.Text));
embed.WithThumbnailUrl(Thumbnail)
.WithImageUrl(Image);
embed.WithFooter(efb =>
{
efb.WithText(Footer.Text);
if (Uri.IsWellFormedUriString(Footer.IconUrl, UriKind.Absolute))
efb.WithIconUrl(Footer.IconUrl);
});
if (Thumbnail != null && Uri.IsWellFormedUriString(Thumbnail, UriKind.Absolute))
embed.WithThumbnailUrl(Thumbnail);
if(Image != null && Uri.IsWellFormedUriString(Image, UriKind.Absolute))
embed.WithImageUrl(Image);
if (Fields != null)
foreach (var f in Fields)
{
embed.AddField(efb => efb.WithName(f.Name).WithValue(f.Value).WithIsInline(f.Inline));
if(!string.IsNullOrWhiteSpace(f.Name) && !string.IsNullOrWhiteSpace(f.Value))
embed.AddField(efb => efb.WithName(f.Name).WithValue(f.Value).WithIsInline(f.Inline));
}
return embed;
@ -58,7 +72,13 @@ namespace NadekoBot.DataStructures
try
{
var crembed = JsonConvert.DeserializeObject<CREmbed>(input);
if(crembed.Fields != null && crembed.Fields.Length > 0)
foreach (var f in crembed.Fields)
{
f.Name = f.Name.TrimTo(256);
f.Value = f.Value.TrimTo(1024);
}
if (!crembed.IsValid)
return false;

View File

@ -1,13 +1,14 @@
// License MIT
// Source: https://github.com/i3arnon/ConcurrentHashSet
using ConcurrentCollections;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
namespace System.Collections.Concurrent
namespace NadekoBot.Common.Collections
{
/// <summary>
/// Represents a thread-safe hash-based unique collection.

View File

@ -2,9 +2,8 @@
using System.Collections;
using System.Collections.Generic;
namespace NadekoBot.DataStructures
namespace NadekoBot.Common.Collections
{
public static class DisposableReadOnlyListExtensions
{
public static IDisposableReadOnlyList<T> AsDisposable<T>(this IReadOnlyList<T> arr) where T : IDisposable

View File

@ -1,11 +1,11 @@
using NadekoBot.Services.Database.Models;
using System.Collections;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using NadekoBot.Services.Database.Models;
namespace NadekoBot.DataStructures
namespace NadekoBot.Common.Collections
{
public class IndexedCollection<T> : IList<T> where T : IIndexed
public class IndexedCollection<T> : IList<T> where T : class, IIndexed
{
public List<T> Source { get; }
private readonly object _locker = new object();

View File

@ -0,0 +1,106 @@
using System;
using System.Threading;
namespace NadekoBot.Common.Collections
{
public class PoopyRingBuffer : IDisposable
{
// readpos == writepos means empty
// writepos == readpos - 1 means full
private byte[] _buffer;
public int Capacity { get; }
private int ReadPos { get; set; } = 0;
private int WritePos { get; set; } = 0;
public int Length => ReadPos <= WritePos
? WritePos - ReadPos
: Capacity - (ReadPos - WritePos);
public int RemainingCapacity
{
get => Capacity - Length - 1;
}
private readonly SemaphoreSlim _locker = new SemaphoreSlim(1, 1);
public PoopyRingBuffer(int capacity = 81920 * 100)
{
this.Capacity = capacity + 1;
this._buffer = new byte[this.Capacity];
}
public int Read(byte[] b, int offset, int toRead)
{
if (WritePos == ReadPos)
return 0;
if (toRead > Length)
toRead = Length;
if (WritePos > ReadPos)
{
Array.Copy(_buffer, ReadPos, b, offset, toRead);
ReadPos += toRead;
}
else
{
var toEnd = Capacity - ReadPos;
var firstRead = toRead > toEnd ?
toEnd :
toRead;
Array.Copy(_buffer, ReadPos, b, offset, firstRead);
ReadPos += firstRead;
var secondRead = toRead - firstRead;
if (secondRead > 0)
{
Array.Copy(_buffer, 0, b, offset + firstRead, secondRead);
ReadPos = secondRead;
}
}
return toRead;
}
public bool Write(byte[] b, int offset, int toWrite)
{
while (toWrite > RemainingCapacity)
return false;
if (toWrite == 0)
return true;
if (WritePos < ReadPos)
{
Array.Copy(b, offset, _buffer, WritePos, toWrite);
WritePos += toWrite;
}
else
{
var toEnd = Capacity - WritePos;
var firstWrite = toWrite > toEnd ?
toEnd :
toWrite;
Array.Copy(b, offset, _buffer, WritePos, firstWrite);
var secondWrite = toWrite - firstWrite;
if (secondWrite > 0)
{
Array.Copy(b, offset + firstWrite, _buffer, 0, secondWrite);
WritePos = secondWrite;
}
else
{
WritePos += firstWrite;
if (WritePos == Capacity)
WritePos = 0;
}
}
return true;
}
public void Dispose()
{
_buffer = null;
}
}
}

View File

@ -0,0 +1,9 @@
namespace NadekoBot.Common
{
public class CommandData
{
public string Cmd { get; set; }
public string Desc { get; set; }
public string[] Usage { get; set; }
}
}

View File

@ -1,7 +1,7 @@
using Discord;
using System.Threading.Tasks;
using System.Threading.Tasks;
using Discord;
namespace NadekoBot.DataStructures.ModuleBehaviors
namespace NadekoBot.Common.ModuleBehaviors
{
/// <summary>
/// Implemented by modules which block execution before anything is executed

View File

@ -1,8 +1,8 @@
using Discord;
using System.Threading.Tasks;
using Discord;
using Discord.WebSocket;
using System.Threading.Tasks;
namespace NadekoBot.DataStructures.ModuleBehaviors
namespace NadekoBot.Common.ModuleBehaviors
{
/// <summary>
/// Implemented by modules which can execute something and prevent further commands from being executed.
@ -13,6 +13,6 @@ namespace NadekoBot.DataStructures.ModuleBehaviors
/// Try to execute some logic within some module's service.
/// </summary>
/// <returns>Whether it should block other command executions after it.</returns>
Task<bool> TryExecuteEarly(DiscordShardedClient client, IGuild guild, IUserMessage msg);
Task<bool> TryExecuteEarly(DiscordSocketClient client, IGuild guild, IUserMessage msg);
}
}

View File

@ -1,4 +1,4 @@
namespace NadekoBot.DataStructures.ModuleBehaviors
namespace NadekoBot.Common.ModuleBehaviors
{
public interface IEarlyExecutor
{

View File

@ -1,7 +1,7 @@
using Discord;
using System.Threading.Tasks;
using System.Threading.Tasks;
using Discord;
namespace NadekoBot.DataStructures.ModuleBehaviors
namespace NadekoBot.Common.ModuleBehaviors
{
public interface IInputTransformer
{

View File

@ -0,0 +1,12 @@
using System.Threading.Tasks;
using Discord;
using Discord.WebSocket;
namespace NadekoBot.Common.ModuleBehaviors
{
public interface ILateBlocker
{
Task<bool> TryBlockLate(DiscordSocketClient client, IUserMessage msg, IGuild guild,
IMessageChannel channel, IUser user, string moduleName, string commandName);
}
}

View File

@ -1,4 +1,4 @@
namespace NadekoBot.DataStructures.ModuleBehaviors
namespace NadekoBot.Common.ModuleBehaviors
{
public interface ILateBlockingExecutor
{

View File

@ -0,0 +1,14 @@
using System.Threading.Tasks;
using Discord;
using Discord.WebSocket;
namespace NadekoBot.Common.ModuleBehaviors
{
/// <summary>
/// Last thing to be executed, won't stop further executions
/// </summary>
public interface ILateExecutor
{
Task LateExecute(DiscordSocketClient client, IGuild guild, IUserMessage msg);
}
}

View File

@ -1,26 +1,21 @@
using System;
using System.Security.Cryptography;
namespace NadekoBot.Services
namespace NadekoBot.Common
{
public class NadekoRandom : Random
{
RandomNumberGenerator rng;
readonly RandomNumberGenerator _rng;
public NadekoRandom() : base()
{
rng = RandomNumberGenerator.Create();
}
private NadekoRandom(int Seed) : base(Seed)
{
rng = RandomNumberGenerator.Create();
_rng = RandomNumberGenerator.Create();
}
public override int Next()
{
var bytes = new byte[sizeof(int)];
rng.GetBytes(bytes);
_rng.GetBytes(bytes);
return Math.Abs(BitConverter.ToInt32(bytes, 0));
}
@ -29,7 +24,7 @@ namespace NadekoBot.Services
if (maxValue <= 0)
throw new ArgumentOutOfRangeException();
var bytes = new byte[sizeof(int)];
rng.GetBytes(bytes);
_rng.GetBytes(bytes);
return Math.Abs(BitConverter.ToInt32(bytes, 0)) % maxValue;
}
@ -40,27 +35,27 @@ namespace NadekoBot.Services
if (minValue == maxValue)
return minValue;
var bytes = new byte[sizeof(int)];
rng.GetBytes(bytes);
_rng.GetBytes(bytes);
var sign = Math.Sign(BitConverter.ToInt32(bytes, 0));
return (sign * BitConverter.ToInt32(bytes, 0)) % (maxValue - minValue) + minValue;
}
public override void NextBytes(byte[] buffer)
{
rng.GetBytes(buffer);
_rng.GetBytes(buffer);
}
protected override double Sample()
{
var bytes = new byte[sizeof(double)];
rng.GetBytes(bytes);
_rng.GetBytes(bytes);
return Math.Abs(BitConverter.ToDouble(bytes, 0) / double.MaxValue + 1);
}
public override double NextDouble()
{
var bytes = new byte[sizeof(double)];
rng.GetBytes(bytes);
_rng.GetBytes(bytes);
return BitConverter.ToDouble(bytes, 0);
}
}

View File

@ -1,8 +1,8 @@
using Discord.Commands;
using System;
using System;
using System.Threading.Tasks;
using Discord.Commands;
namespace NadekoBot.DataStructures
namespace NadekoBot.Common
{
public class NoPublicBot : PreconditionAttribute
{

View File

@ -1,6 +1,6 @@
using System;
namespace ConcurrentCollections
namespace NadekoBot.Common
{
public static class PlatformHelper
{

View File

@ -0,0 +1,147 @@
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Text.RegularExpressions;
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using NadekoBot.Extensions;
using NadekoBot.Modules.Administration.Services;
using NadekoBot.Modules.Music.Services;
namespace NadekoBot.Common.Replacements
{
public class ReplacementBuilder
{
private static readonly Regex rngRegex = new Regex("%rng(?:(?<from>(?:-)?\\d+)-(?<to>(?:-)?\\d+))?%", RegexOptions.Compiled);
private ConcurrentDictionary<string, Func<string>> _reps = new ConcurrentDictionary<string, Func<string>>();
private ConcurrentDictionary<Regex, Func<Match, string>> _regex = new ConcurrentDictionary<Regex, Func<Match, string>>();
public ReplacementBuilder()
{
WithRngRegex();
}
public ReplacementBuilder WithDefault(IUser usr, IMessageChannel ch, IGuild g, DiscordSocketClient client)
{
return this.WithUser(usr)
.WithChannel(ch)
.WithServer(client, g)
.WithClient(client);
}
public ReplacementBuilder WithDefault(ICommandContext ctx) =>
WithDefault(ctx.User, ctx.Channel, ctx.Guild, (DiscordSocketClient)ctx.Client);
public ReplacementBuilder WithClient(DiscordSocketClient client)
{
_reps.TryAdd("%mention%", () => $"<@{client.CurrentUser.Id}>");
_reps.TryAdd("%shardid%", () => client.ShardId.ToString());
_reps.TryAdd("%time%", () => DateTime.Now.ToString("HH:mm " + TimeZoneInfo.Local.StandardName.GetInitials()));
return this;
}
public ReplacementBuilder WithServer(DiscordSocketClient client, IGuild g)
{
_reps.TryAdd("%sid%", () => g == null ? "DM" : g.Id.ToString());
_reps.TryAdd("%server%", () => g == null ? "DM" : g.Name);
_reps.TryAdd("%server_time%", () =>
{
TimeZoneInfo to = TimeZoneInfo.Local;
if (g != null)
{
if (GuildTimezoneService.AllServices.TryGetValue(client.CurrentUser.Id, out var tz))
to = tz.GetTimeZoneOrDefault(g.Id) ?? TimeZoneInfo.Local;
}
return TimeZoneInfo.ConvertTime(DateTime.UtcNow,
TimeZoneInfo.Utc,
to).ToString("HH:mm ") + to.StandardName.GetInitials();
});
return this;
}
public ReplacementBuilder WithChannel(IMessageChannel ch)
{
_reps.TryAdd("%channel%", () => (ch as ITextChannel)?.Mention ?? "#" + ch.Name);
_reps.TryAdd("%chname%", () => ch.Name);
_reps.TryAdd("%cid%", () => ch?.Id.ToString());
return this;
}
public ReplacementBuilder WithUser(IUser user)
{
_reps.TryAdd("%user%", () => user.Mention);
_reps.TryAdd("%userfull%", () => user.ToString());
_reps.TryAdd("%username%", () => user.Username);
_reps.TryAdd("%userdiscrim%", () => user.Discriminator);
_reps.TryAdd("%id%", () => user.Id.ToString());
_reps.TryAdd("%uid%", () => user.Id.ToString());
return this;
}
public ReplacementBuilder WithStats(DiscordSocketClient c)
{
_reps.TryAdd("%servers%", () => c.Guilds.Count.ToString());
_reps.TryAdd("%users%", () => c.Guilds.Sum(s => s.Users.Count).ToString());
return this;
}
public ReplacementBuilder WithMusic(MusicService ms)
{
_reps.TryAdd("%playing%", () =>
{
var cnt = ms.MusicPlayers.Count(kvp => kvp.Value.Current.Current != null);
if (cnt != 1) return cnt.ToString();
try
{
var mp = ms.MusicPlayers.FirstOrDefault();
var title = mp.Value?.Current.Current?.Title;
return title ?? "No songs";
}
catch
{
return "error";
}
});
_reps.TryAdd("%queued%", () => ms.MusicPlayers.Sum(kvp => kvp.Value.QueueArray().Songs.Length).ToString());
return this;
}
public ReplacementBuilder WithRngRegex()
{
var rng = new NadekoRandom();
_regex.TryAdd(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 string.Empty;
return rng.Next(from, to + 1).ToString();
});
return this;
}
public ReplacementBuilder WithOverride(string key, Func<string> output)
{
_reps.AddOrUpdate(key, output, delegate { return output; });
return this;
}
public Replacer Build()
{
return new Replacer(_reps.Select(x => (x.Key, x.Value)).ToArray(), _regex.Select(x => (x.Key, x.Value)).ToArray());
}
}
}

View File

@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace NadekoBot.Common.Replacements
{
public class Replacer
{
private readonly IEnumerable<(string Key, Func<string> Text)> _replacements;
private readonly IEnumerable<(Regex Regex, Func<Match, string> Replacement)> _regex;
public Replacer(IEnumerable<(string, Func<string>)> replacements, IEnumerable<(Regex, Func<Match, string>)> regex)
{
_replacements = replacements;
_regex = regex;
}
public string Replace(string input)
{
if (string.IsNullOrWhiteSpace(input))
return input;
foreach (var item in _replacements)
{
if (input.Contains(item.Key))
input = input.Replace(item.Key, item.Text());
}
foreach (var item in _regex)
{
input = item.Regex.Replace(input, (m) => item.Replacement(m));
}
return input;
}
public void Replace(CREmbed embedData)
{
embedData.PlainText = Replace(embedData.PlainText);
embedData.Description = Replace(embedData.Description);
embedData.Title = Replace(embedData.Title);
if (embedData.Fields != null)
foreach (var f in embedData.Fields)
{
f.Name = Replace(f.Name);
f.Value = Replace(f.Value);
}
if (embedData.Footer != null)
embedData.Footer.Text = Replace(embedData.Footer.Text);
}
}
}

View File

@ -0,0 +1,19 @@
using System;
using System.Threading.Tasks;
using Discord.Commands;
using Discord.WebSocket;
namespace NadekoBot.Common
{
public class Shard0Precondition : PreconditionAttribute
{
public override Task<PreconditionResult> CheckPermissions(ICommandContext context, CommandInfo command, IServiceProvider services)
{
var c = (DiscordSocketClient)context.Client;
if (c.ShardId != 0)
return Task.FromResult(PreconditionResult.FromError("Must be ran from shard #0"));
return Task.FromResult(PreconditionResult.FromSuccess());
}
}
}

View File

@ -0,0 +1,13 @@
using System;
using Discord;
namespace NadekoBot.Common.ShardCom
{
public class ShardComMessage
{
public int ShardId { get; set; }
public ConnectionState ConnectionState { get; set; }
public int Guilds { get; set; }
public DateTime Time { get; set; }
}
}

View File

@ -0,0 +1,28 @@
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace NadekoBot.Common.ShardCom
{
public class ShardComClient
{
private int port;
public ShardComClient(int port)
{
this.port = port;
}
public async Task Send(ShardComMessage data)
{
var msg = JsonConvert.SerializeObject(data);
using (var client = new UdpClient())
{
var bytes = Encoding.UTF8.GetBytes(msg);
await client.SendAsync(bytes, bytes.Length, IPAddress.Loopback.ToString(), port).ConfigureAwait(false);
}
}
}
}

View File

@ -0,0 +1,40 @@
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace NadekoBot.Common.ShardCom
{
public class ShardComServer : IDisposable
{
private readonly UdpClient _client;
public ShardComServer(int port)
{
_client = new UdpClient(port);
}
public void Start()
{
Task.Run(async () =>
{
var ip = new IPEndPoint(IPAddress.Any, 0);
while (true)
{
var recv = await _client.ReceiveAsync();
var data = Encoding.UTF8.GetString(recv.Buffer);
var _ = OnDataReceived(JsonConvert.DeserializeObject<ShardComMessage>(data));
}
});
}
public void Dispose()
{
_client.Dispose();
}
public event Func<ShardComMessage, Task> OnDataReceived = delegate { return Task.CompletedTask; };
}
}

View File

@ -0,0 +1,9 @@
namespace NadekoBot.Common.TypeReaders
{
public enum AddRemove
{
Add = 0,
Rem = 1,
Rm = 1,
}
}

View File

@ -1,24 +1,18 @@
using Discord.Commands;
using NadekoBot.Services;
using NadekoBot.Services.CustomReactions;
using System;
using System.Linq;
using System.Threading.Tasks;
using Discord.Commands;
using NadekoBot.Modules.CustomReactions.Services;
using NadekoBot.Services;
namespace NadekoBot.TypeReaders
namespace NadekoBot.Common.TypeReaders
{
public class CommandTypeReader : TypeReader
{
private readonly CommandService _cmds;
private readonly CommandHandler _cmdHandler;
public CommandTypeReader(CommandService cmds, CommandHandler cmdHandler)
{
_cmds = cmds;
_cmdHandler = cmdHandler;
}
public override Task<TypeReaderResult> Read(ICommandContext context, string input)
public override Task<TypeReaderResult> Read(ICommandContext context, string input, IServiceProvider services)
{
var _cmds = ((INServiceProvider)services).GetService<CommandService>();
var _cmdHandler = ((INServiceProvider)services).GetService<CommandHandler>();
input = input.ToUpperInvariant();
var prefix = _cmdHandler.GetPrefix(context.Guild);
if (!input.StartsWith(prefix.ToUpperInvariant()))
@ -37,17 +31,12 @@ namespace NadekoBot.TypeReaders
public class CommandOrCrTypeReader : CommandTypeReader
{
private readonly CustomReactionsService _crs;
public CommandOrCrTypeReader(CustomReactionsService crs, CommandService cmds, CommandHandler cmdHandler) : base(cmds, cmdHandler)
{
_crs = crs;
}
public override async Task<TypeReaderResult> Read(ICommandContext context, string input)
public override async Task<TypeReaderResult> Read(ICommandContext context, string input, IServiceProvider services)
{
input = input.ToUpperInvariant();
var _crs = ((INServiceProvider)services).GetService<CustomReactionsService>();
if (_crs.GlobalReactions.Any(x => x.Trigger.ToUpperInvariant() == input))
{
return TypeReaderResult.FromSuccess(new CommandOrCrInfo(input));
@ -64,7 +53,7 @@ namespace NadekoBot.TypeReaders
}
}
var cmd = await base.Read(context, input);
var cmd = await base.Read(context, input, services);
if (cmd.IsSuccess)
{
return TypeReaderResult.FromSuccess(new CommandOrCrInfo(((CommandInfo)cmd.Values.First().Value).Name));

View File

@ -1,21 +1,15 @@
using Discord.Commands;
using NadekoBot.Services.Administration;
using System;
using System;
using System.Threading.Tasks;
using Discord.Commands;
using NadekoBot.Modules.Administration.Services;
namespace NadekoBot.TypeReaders
namespace NadekoBot.Common.TypeReaders
{
public class GuildDateTimeTypeReader : TypeReader
{
private readonly GuildTimezoneService _gts;
public GuildDateTimeTypeReader(GuildTimezoneService gts)
{
_gts = gts;
}
public override Task<TypeReaderResult> Read(ICommandContext context, string input)
public override Task<TypeReaderResult> Read(ICommandContext context, string input, IServiceProvider services)
{
var _gts = (GuildTimezoneService)services.GetService(typeof(GuildTimezoneService));
if (!DateTime.TryParse(input, out var dt))
return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "Input string is in an incorrect format."));

View File

@ -1,19 +1,20 @@
using Discord.Commands;
using Discord.WebSocket;
using System;
using System.Linq;
using System.Threading.Tasks;
using Discord.Commands;
using Discord.WebSocket;
namespace NadekoBot.TypeReaders
namespace NadekoBot.Common.TypeReaders
{
public class GuildTypeReader : TypeReader
{
private readonly DiscordShardedClient _client;
private readonly DiscordSocketClient _client;
public GuildTypeReader(DiscordShardedClient client)
public GuildTypeReader(DiscordSocketClient client)
{
_client = client;
}
public override Task<TypeReaderResult> Read(ICommandContext context, string input)
public override Task<TypeReaderResult> Read(ICommandContext context, string input, IServiceProvider _)
{
input = input.Trim().ToLowerInvariant();
var guilds = _client.Guilds;

View File

@ -1,4 +1,4 @@
namespace NadekoBot.Modules.Permissions
namespace NadekoBot.Common.TypeReaders.Models
{
public class PermissionAction
{

View File

@ -1,9 +1,10 @@
using Discord.Commands;
using NadekoBot.Extensions;
using System;
using System.Linq;
using System.Threading.Tasks;
using Discord.Commands;
using NadekoBot.Extensions;
namespace NadekoBot.TypeReaders
namespace NadekoBot.Common.TypeReaders
{
public class ModuleTypeReader : TypeReader
{
@ -14,7 +15,7 @@ namespace NadekoBot.TypeReaders
_cmds = cmds;
}
public override Task<TypeReaderResult> Read(ICommandContext context, string input)
public override Task<TypeReaderResult> Read(ICommandContext context, string input, IServiceProvider _)
{
input = input.ToUpperInvariant();
var module = _cmds.Modules.GroupBy(m => m.GetTopLevelModule()).FirstOrDefault(m => m.Key.Name.ToUpperInvariant() == input)?.Key;
@ -34,7 +35,7 @@ namespace NadekoBot.TypeReaders
_cmds = cmds;
}
public override Task<TypeReaderResult> Read(ICommandContext context, string input)
public override Task<TypeReaderResult> Read(ICommandContext context, string input, IServiceProvider _)
{
input = input.ToLowerInvariant();
var module = _cmds.Modules.GroupBy(m => m.GetTopLevelModule()).FirstOrDefault(m => m.Key.Name.ToLowerInvariant() == input)?.Key;

View File

@ -1,15 +1,16 @@
using Discord.Commands;
using System;
using System.Threading.Tasks;
using NadekoBot.Modules.Permissions;
using Discord.Commands;
using NadekoBot.Common.TypeReaders.Models;
namespace NadekoBot.TypeReaders
namespace NadekoBot.Common.TypeReaders
{
/// <summary>
/// Used instead of bool for more flexible keywords for true/false only in the permission module
/// </summary>
public class PermissionActionTypeReader : TypeReader
{
public override Task<TypeReaderResult> Read(ICommandContext context, string input)
public override Task<TypeReaderResult> Read(ICommandContext context, string input, IServiceProvider _)
{
input = input.ToUpperInvariant();
switch (input)

View File

@ -1,12 +0,0 @@
using Discord;
using Discord.WebSocket;
using System.Threading.Tasks;
namespace NadekoBot.DataStructures.ModuleBehaviors
{
public interface ILateBlocker
{
Task<bool> TryBlockLate(DiscordShardedClient client, IUserMessage msg, IGuild guild,
IMessageChannel channel, IUser user, string moduleName, string commandName);
}
}

View File

@ -1,14 +0,0 @@
using Discord;
using Discord.WebSocket;
using System.Threading.Tasks;
namespace NadekoBot.DataStructures.ModuleBehaviors
{
/// <summary>
/// Last thing to be executed, won't stop further executions
/// </summary>
public interface ILateExecutor
{
Task LateExecute(DiscordShardedClient client, IGuild guild, IUserMessage msg);
}
}

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

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

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

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

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,45 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
namespace NadekoBot.Migrations
{
public partial class streamrole : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "StreamRoleSettings",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("Sqlite:Autoincrement", true),
AddRoleId = table.Column<ulong>(nullable: false),
DateAdded = table.Column<DateTime>(nullable: true),
FromRoleId = table.Column<ulong>(nullable: false),
GuildConfigId = table.Column<int>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_StreamRoleSettings", x => x.Id);
table.ForeignKey(
name: "FK_StreamRoleSettings_GuildConfigs_GuildConfigId",
column: x => x.GuildConfigId,
principalTable: "GuildConfigs",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_StreamRoleSettings_GuildConfigId",
table: "StreamRoleSettings",
column: "GuildConfigId",
unique: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "StreamRoleSettings");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,93 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
namespace NadekoBot.Migrations
{
public partial class streamrolekwblwl : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<bool>(
name: "Enabled",
table: "StreamRoleSettings",
nullable: false,
defaultValue: false);
migrationBuilder.AddColumn<string>(
name: "Keyword",
table: "StreamRoleSettings",
nullable: true);
migrationBuilder.CreateTable(
name: "StreamRoleBlacklistedUser",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("Sqlite:Autoincrement", true),
DateAdded = table.Column<DateTime>(nullable: true),
StreamRoleSettingsId = table.Column<int>(nullable: true),
UserId = table.Column<ulong>(nullable: false),
Username = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_StreamRoleBlacklistedUser", x => x.Id);
table.ForeignKey(
name: "FK_StreamRoleBlacklistedUser_StreamRoleSettings_StreamRoleSettingsId",
column: x => x.StreamRoleSettingsId,
principalTable: "StreamRoleSettings",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateTable(
name: "StreamRoleWhitelistedUser",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("Sqlite:Autoincrement", true),
DateAdded = table.Column<DateTime>(nullable: true),
StreamRoleSettingsId = table.Column<int>(nullable: true),
UserId = table.Column<ulong>(nullable: false),
Username = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_StreamRoleWhitelistedUser", x => x.Id);
table.ForeignKey(
name: "FK_StreamRoleWhitelistedUser_StreamRoleSettings_StreamRoleSettingsId",
column: x => x.StreamRoleSettingsId,
principalTable: "StreamRoleSettings",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateIndex(
name: "IX_StreamRoleBlacklistedUser_StreamRoleSettingsId",
table: "StreamRoleBlacklistedUser",
column: "StreamRoleSettingsId");
migrationBuilder.CreateIndex(
name: "IX_StreamRoleWhitelistedUser_StreamRoleSettingsId",
table: "StreamRoleWhitelistedUser",
column: "StreamRoleSettingsId");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "StreamRoleBlacklistedUser");
migrationBuilder.DropTable(
name: "StreamRoleWhitelistedUser");
migrationBuilder.DropColumn(
name: "Enabled",
table: "StreamRoleSettings");
migrationBuilder.DropColumn(
name: "Keyword",
table: "StreamRoleSettings");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Migrations;
namespace NadekoBot.Migrations
{
public partial class nsfwblacklist : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "NsfwBlacklitedTag",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("Sqlite:Autoincrement", true),
DateAdded = table.Column<DateTime>(nullable: true),
GuildConfigId = table.Column<int>(nullable: true),
Tag = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_NsfwBlacklitedTag", x => x.Id);
table.ForeignKey(
name: "FK_NsfwBlacklitedTag_GuildConfigs_GuildConfigId",
column: x => x.GuildConfigId,
principalTable: "GuildConfigs",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateIndex(
name: "IX_NsfwBlacklitedTag_GuildConfigId",
table: "NsfwBlacklitedTag",
column: "GuildConfigId");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "NsfwBlacklitedTag");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Migrations;
namespace NadekoBot.Migrations
{
public partial class crca : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<bool>(
name: "ContainsAnywhere",
table: "CustomReactions",
nullable: false,
defaultValue: false);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "ContainsAnywhere",
table: "CustomReactions");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Migrations;
namespace NadekoBot.Migrations
{
public partial class waifuitems : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "WaifuItem",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("Sqlite:Autoincrement", true),
DateAdded = table.Column<DateTime>(nullable: true),
Item = table.Column<int>(nullable: false),
ItemEmoji = table.Column<string>(nullable: true),
Price = table.Column<int>(nullable: false),
WaifuInfoId = table.Column<int>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_WaifuItem", x => x.Id);
table.ForeignKey(
name: "FK_WaifuItem_WaifuInfo_WaifuInfoId",
column: x => x.WaifuInfoId,
principalTable: "WaifuInfo",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateIndex(
name: "IX_WaifuItem_WaifuInfoId",
table: "WaifuItem",
column: "WaifuInfoId");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "WaifuItem");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Migrations;
namespace NadekoBot.Migrations
{
public partial class mutetimeantispam : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "MuteTime",
table: "AntiSpamSetting",
nullable: false,
defaultValue: 0);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "MuteTime",
table: "AntiSpamSetting");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,296 @@
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Migrations;
namespace NadekoBot.Migrations
{
public partial class xpandclubs : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "XpMinutesTimeout",
table: "BotConfig",
nullable: false,
defaultValue: 5)
.Annotation("Sqlite:Autoincrement", true);
migrationBuilder.AddColumn<int>(
name: "XpPerMessage",
table: "BotConfig",
nullable: false,
defaultValue: 3)
.Annotation("Sqlite:Autoincrement", true);
migrationBuilder.CreateTable(
name: "Clubs",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("Sqlite:Autoincrement", true),
DateAdded = table.Column<DateTime>(nullable: true),
Discrim = table.Column<int>(nullable: false),
ImageUrl = table.Column<string>(nullable: true),
MinimumLevelReq = table.Column<int>(nullable: false),
Name = table.Column<string>(maxLength: 20, nullable: false),
OwnerId = table.Column<int>(nullable: false),
Xp = table.Column<int>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Clubs", x => x.Id);
table.UniqueConstraint("AK_Clubs_Name_Discrim", x => new { x.Name, x.Discrim });
table.ForeignKey(
name: "FK_Clubs_DiscordUser_OwnerId",
column: x => x.OwnerId,
principalTable: "DiscordUser",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.Sql(MigrationQueries.UserClub);
migrationBuilder.AddColumn<DateTime>(
name: "LastLevelUp",
table: "DiscordUser",
nullable: false,
defaultValue: DateTime.UtcNow);
migrationBuilder.AddColumn<int>(
name: "NotifyOnLevelUp",
table: "DiscordUser",
nullable: false,
defaultValue: 0);
migrationBuilder.CreateTable(
name: "UserXpStats",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("Sqlite:Autoincrement", true),
AwardedXp = table.Column<int>(nullable: false),
DateAdded = table.Column<DateTime>(nullable: true),
GuildId = table.Column<ulong>(nullable: false),
LastLevelUp = table.Column<DateTime>(nullable: false, defaultValue: new DateTime(2017, 9, 9, 1, 7, 29, 858, DateTimeKind.Local)),
NotifyOnLevelUp = table.Column<int>(nullable: false),
UserId = table.Column<ulong>(nullable: false),
Xp = table.Column<int>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_UserXpStats", x => x.Id);
});
migrationBuilder.CreateTable(
name: "XpSettings",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("Sqlite:Autoincrement", true),
DateAdded = table.Column<DateTime>(nullable: true),
GuildConfigId = table.Column<int>(nullable: false),
NotifyMessage = table.Column<string>(nullable: true),
ServerExcluded = table.Column<bool>(nullable: false),
XpRoleRewardExclusive = table.Column<bool>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_XpSettings", x => x.Id);
table.ForeignKey(
name: "FK_XpSettings_GuildConfigs_GuildConfigId",
column: x => x.GuildConfigId,
principalTable: "GuildConfigs",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "ClubApplicants",
columns: table => new
{
ClubId = table.Column<int>(nullable: false),
UserId = table.Column<int>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ClubApplicants", x => new { x.ClubId, x.UserId });
table.ForeignKey(
name: "FK_ClubApplicants_Clubs_ClubId",
column: x => x.ClubId,
principalTable: "Clubs",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_ClubApplicants_DiscordUser_UserId",
column: x => x.UserId,
principalTable: "DiscordUser",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "ClubBans",
columns: table => new
{
ClubId = table.Column<int>(nullable: false),
UserId = table.Column<int>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ClubBans", x => new { x.ClubId, x.UserId });
table.ForeignKey(
name: "FK_ClubBans_Clubs_ClubId",
column: x => x.ClubId,
principalTable: "Clubs",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_ClubBans_DiscordUser_UserId",
column: x => x.UserId,
principalTable: "DiscordUser",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "ExcludedItem",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("Sqlite:Autoincrement", true),
DateAdded = table.Column<DateTime>(nullable: true),
ItemId = table.Column<ulong>(nullable: false),
ItemType = table.Column<int>(nullable: false),
XpSettingsId = table.Column<int>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_ExcludedItem", x => x.Id);
table.ForeignKey(
name: "FK_ExcludedItem_XpSettings_XpSettingsId",
column: x => x.XpSettingsId,
principalTable: "XpSettings",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateTable(
name: "XpRoleReward",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("Sqlite:Autoincrement", true),
DateAdded = table.Column<DateTime>(nullable: true),
Level = table.Column<int>(nullable: false),
RoleId = table.Column<ulong>(nullable: false),
XpSettingsId = table.Column<int>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_XpRoleReward", x => x.Id);
table.UniqueConstraint("AK_XpRoleReward_Level", x => x.Level);
table.ForeignKey(
name: "FK_XpRoleReward_XpSettings_XpSettingsId",
column: x => x.XpSettingsId,
principalTable: "XpSettings",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateIndex(
name: "IX_DiscordUser_ClubId",
table: "DiscordUser",
column: "ClubId");
migrationBuilder.CreateIndex(
name: "IX_ClubApplicants_UserId",
table: "ClubApplicants",
column: "UserId");
migrationBuilder.CreateIndex(
name: "IX_ClubBans_UserId",
table: "ClubBans",
column: "UserId");
migrationBuilder.CreateIndex(
name: "IX_Clubs_OwnerId",
table: "Clubs",
column: "OwnerId",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_ExcludedItem_XpSettingsId",
table: "ExcludedItem",
column: "XpSettingsId");
migrationBuilder.CreateIndex(
name: "IX_UserXpStats_UserId_GuildId",
table: "UserXpStats",
columns: new[] { "UserId", "GuildId" },
unique: true);
migrationBuilder.CreateIndex(
name: "IX_XpRoleReward_XpSettingsId",
table: "XpRoleReward",
column: "XpSettingsId");
migrationBuilder.CreateIndex(
name: "IX_XpSettings_GuildConfigId",
table: "XpSettings",
column: "GuildConfigId",
unique: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_DiscordUser_Clubs_ClubId",
table: "DiscordUser");
migrationBuilder.DropTable(
name: "ClubApplicants");
migrationBuilder.DropTable(
name: "ClubBans");
migrationBuilder.DropTable(
name: "ExcludedItem");
migrationBuilder.DropTable(
name: "UserXpStats");
migrationBuilder.DropTable(
name: "XpRoleReward");
migrationBuilder.DropTable(
name: "Clubs");
migrationBuilder.DropTable(
name: "XpSettings");
migrationBuilder.DropIndex(
name: "IX_DiscordUser_ClubId",
table: "DiscordUser");
migrationBuilder.DropColumn(
name: "ClubId",
table: "DiscordUser");
migrationBuilder.DropColumn(
name: "LastLevelUp",
table: "DiscordUser");
migrationBuilder.DropColumn(
name: "NotifyOnLevelUp",
table: "DiscordUser");
migrationBuilder.DropColumn(
name: "XpMinutesTimeout",
table: "BotConfig");
migrationBuilder.DropColumn(
name: "XpPerMessage",
table: "BotConfig");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Migrations;
namespace NadekoBot.Migrations
{
public partial class lastXpGain : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<DateTime>(
name: "LastXpGain",
table: "DiscordUser",
nullable: false,
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
migrationBuilder.Sql("DELETE FROM XpRoleReward WHERE XpSettingsId is null");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "LastXpGain",
table: "DiscordUser");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Migrations;
namespace NadekoBot.Migrations
{
public partial class totalxp : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "TotalXp",
table: "DiscordUser",
nullable: false,
defaultValue: 0);
migrationBuilder.Sql(MigrationQueries.TotalXp);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "TotalXp",
table: "DiscordUser");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Migrations;
namespace NadekoBot.Migrations
{
public partial class clubadmins : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<bool>(
name: "IsClubAdmin",
table: "DiscordUser",
nullable: false,
defaultValue: false);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "IsClubAdmin",
table: "DiscordUser");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,59 @@
using Microsoft.EntityFrameworkCore.Migrations;
using System;
using System.Collections.Generic;
namespace NadekoBot.Migrations
{
public partial class feeds : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "FeedSub",
columns: table => new
{
Id = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
ChannelId = table.Column<ulong>(type: "INTEGER", nullable: false),
DateAdded = table.Column<DateTime>(type: "TEXT", nullable: true),
GuildConfigId = table.Column<int>(type: "INTEGER", nullable: false),
Url = table.Column<string>(type: "TEXT", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_FeedSub", x => x.Id);
table.UniqueConstraint("AK_FeedSub_GuildConfigId_Url", x => new { x.GuildConfigId, x.Url });
table.ForeignKey(
name: "FK_FeedSub_GuildConfigs_GuildConfigId",
column: x => x.GuildConfigId,
principalTable: "GuildConfigs",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "FeedSub");
migrationBuilder.AlterColumn<DateTime>(
name: "LastLevelUp",
table: "UserXpStats",
nullable: false,
defaultValue: new DateTime(2017, 9, 15, 5, 48, 8, 665, DateTimeKind.Local),
oldClrType: typeof(DateTime),
oldType: "TEXT",
oldDefaultValue: new DateTime(2017, 9, 21, 20, 53, 13, 307, DateTimeKind.Local));
migrationBuilder.AlterColumn<DateTime>(
name: "LastLevelUp",
table: "DiscordUser",
nullable: false,
defaultValue: new DateTime(2017, 9, 15, 5, 48, 8, 660, DateTimeKind.Local),
oldClrType: typeof(DateTime),
oldType: "TEXT",
oldDefaultValue: new DateTime(2017, 9, 21, 20, 53, 13, 305, DateTimeKind.Local));
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,47 @@
using Microsoft.EntityFrameworkCore.Migrations;
using System;
using System.Collections.Generic;
namespace NadekoBot.Migrations
{
public partial class xprrfix : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable("XpRoleReward");
migrationBuilder.CreateTable(
name: "XpRoleReward",
columns: table => new
{
Id = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
DateAdded = table.Column<DateTime>(type: "TEXT", nullable: true),
Level = table.Column<int>(type: "INTEGER", nullable: false),
RoleId = table.Column<ulong>(type: "INTEGER", nullable: false),
XpSettingsId = table.Column<int>(type: "INTEGER", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_XpRoleReward", x => x.Id);
table.ForeignKey(
name: "FK_XpRoleReward_XpSettings_XpSettingsId",
column: x => x.XpSettingsId,
principalTable: "XpSettings",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_XpRoleReward_XpSettingsId_Level",
table: "XpRoleReward",
columns: new[] { "XpSettingsId", "Level" },
unique: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
}
}
}

View File

@ -0,0 +1,42 @@
namespace NadekoBot.Migrations
{
internal class MigrationQueries
{
public static string UserClub { get; } = @"
CREATE TABLE DiscordUser_tmp(
Id INTEGER PRIMARY KEY,
AvatarId TEXT,
Discriminator TEXT,
UserId INTEGER UNIQUE NOT NULL,
DateAdded TEXT,
Username TEXT
);
INSERT INTO DiscordUser_tmp
SELECT Id, AvatarId, Discriminator, UserId, DateAdded, Username
FROM DiscordUser;
DROP TABLE DiscordUser;
CREATE TABLE DiscordUser(
Id INTEGER PRIMARY KEY,
AvatarId TEXT,
Discriminator TEXT,
UserId INTEGER UNIQUE NOT NULL,
DateAdded TEXT,
Username TEXT,
ClubId INTEGER,
CONSTRAINT FK_DiscordUser_Clubs_ClubId FOREIGN KEY(ClubId) REFERENCES Clubs(Id) ON DELETE RESTRICT
);
INSERT INTO DiscordUser
SELECT Id, AvatarId, Discriminator, UserId, DateAdded, Username, NULL
FROM DiscordUser_tmp;
DROP TABLE DiscordUser_tmp;";
public static string TotalXp { get; } =
@"UPDATE DiscordUser
SET TotalXp = ifnull((SELECT SUM(Xp) FROM UserXpStats WHERE UserId = DiscordUser.UserId), 0)";
}
}

View File

@ -1,10 +1,13 @@
using System;
// <auto-generated />
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.Storage.Internal;
using NadekoBot.Services.Database;
using NadekoBot.Services.Database.Models;
using System;
namespace NadekoBot.Migrations
{
@ -13,8 +16,9 @@ namespace NadekoBot.Migrations
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "1.1.1");
.HasAnnotation("ProductVersion", "2.0.0-rtm-26452");
modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiRaidSetting", b =>
{
@ -70,6 +74,8 @@ namespace NadekoBot.Migrations
b.Property<int>("MessageThreshold");
b.Property<int>("MuteTime");
b.HasKey("Id");
b.HasIndex("GuildConfigId")
@ -181,6 +187,14 @@ namespace NadekoBot.Migrations
b.Property<int>("TriviaCurrencyReward");
b.Property<int>("XpMinutesTimeout")
.ValueGeneratedOnAdd()
.HasDefaultValue(5);
b.Property<int>("XpPerMessage")
.ValueGeneratedOnAdd()
.HasDefaultValue(3);
b.HasKey("Id");
b.ToTable("BotConfig");
@ -236,6 +250,63 @@ namespace NadekoBot.Migrations
b.ToTable("ClashOfClans");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.ClubApplicants", b =>
{
b.Property<int>("ClubId");
b.Property<int>("UserId");
b.HasKey("ClubId", "UserId");
b.HasIndex("UserId");
b.ToTable("ClubApplicants");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.ClubBans", b =>
{
b.Property<int>("ClubId");
b.Property<int>("UserId");
b.HasKey("ClubId", "UserId");
b.HasIndex("UserId");
b.ToTable("ClubBans");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.ClubInfo", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<DateTime?>("DateAdded");
b.Property<int>("Discrim");
b.Property<string>("ImageUrl");
b.Property<int>("MinimumLevelReq");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(20);
b.Property<int>("OwnerId");
b.Property<int>("Xp");
b.HasKey("Id");
b.HasAlternateKey("Name", "Discrim");
b.HasIndex("OwnerId")
.IsUnique();
b.ToTable("Clubs");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandAlias", b =>
{
b.Property<int>("Id")
@ -361,6 +432,8 @@ namespace NadekoBot.Migrations
b.Property<bool>("AutoDeleteTrigger");
b.Property<bool>("ContainsAnywhere");
b.Property<DateTime?>("DateAdded");
b.Property<bool>("DmResponse");
@ -387,10 +460,24 @@ namespace NadekoBot.Migrations
b.Property<string>("AvatarId");
b.Property<int?>("ClubId");
b.Property<DateTime?>("DateAdded");
b.Property<string>("Discriminator");
b.Property<bool>("IsClubAdmin");
b.Property<DateTime>("LastLevelUp")
.ValueGeneratedOnAdd()
.HasDefaultValue(new DateTime(2017, 9, 21, 20, 53, 13, 305, DateTimeKind.Local));
b.Property<DateTime>("LastXpGain");
b.Property<int>("NotifyOnLevelUp");
b.Property<int>("TotalXp");
b.Property<ulong>("UserId");
b.Property<string>("Username");
@ -399,6 +486,8 @@ namespace NadekoBot.Migrations
b.HasAlternateKey("UserId");
b.HasIndex("ClubId");
b.ToTable("DiscordUser");
});
@ -441,6 +530,47 @@ namespace NadekoBot.Migrations
b.ToTable("EightBallResponses");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.ExcludedItem", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<DateTime?>("DateAdded");
b.Property<ulong>("ItemId");
b.Property<int>("ItemType");
b.Property<int?>("XpSettingsId");
b.HasKey("Id");
b.HasIndex("XpSettingsId");
b.ToTable("ExcludedItem");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.FeedSub", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong>("ChannelId");
b.Property<DateTime?>("DateAdded");
b.Property<int>("GuildConfigId");
b.Property<string>("Url")
.IsRequired();
b.HasKey("Id");
b.HasAlternateKey("GuildConfigId", "Url");
b.ToTable("FeedSub");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterChannelId", b =>
{
b.Property<int>("Id")
@ -800,6 +930,24 @@ namespace NadekoBot.Migrations
b.ToTable("MutedUserId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.NsfwBlacklitedTag", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<DateTime?>("DateAdded");
b.Property<int?>("GuildConfigId");
b.Property<string>("Tag");
b.HasKey("Id");
b.HasIndex("GuildConfigId");
b.ToTable("NsfwBlacklitedTag");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.Permission", b =>
{
b.Property<int>("Id")
@ -1126,6 +1274,71 @@ namespace NadekoBot.Migrations
b.ToTable("StartupCommand");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.StreamRoleBlacklistedUser", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<DateTime?>("DateAdded");
b.Property<int?>("StreamRoleSettingsId");
b.Property<ulong>("UserId");
b.Property<string>("Username");
b.HasKey("Id");
b.HasIndex("StreamRoleSettingsId");
b.ToTable("StreamRoleBlacklistedUser");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.StreamRoleSettings", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong>("AddRoleId");
b.Property<DateTime?>("DateAdded");
b.Property<bool>("Enabled");
b.Property<ulong>("FromRoleId");
b.Property<int>("GuildConfigId");
b.Property<string>("Keyword");
b.HasKey("Id");
b.HasIndex("GuildConfigId")
.IsUnique();
b.ToTable("StreamRoleSettings");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.StreamRoleWhitelistedUser", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<DateTime?>("DateAdded");
b.Property<int?>("StreamRoleSettingsId");
b.Property<ulong>("UserId");
b.Property<string>("Username");
b.HasKey("Id");
b.HasIndex("StreamRoleSettingsId");
b.ToTable("StreamRoleWhitelistedUser");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.UnmuteTimer", b =>
{
b.Property<int>("Id")
@ -1165,6 +1378,35 @@ namespace NadekoBot.Migrations
b.ToTable("PokeGame");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.UserXpStats", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("AwardedXp");
b.Property<DateTime?>("DateAdded");
b.Property<ulong>("GuildId");
b.Property<DateTime>("LastLevelUp")
.ValueGeneratedOnAdd()
.HasDefaultValue(new DateTime(2017, 9, 21, 20, 53, 13, 307, DateTimeKind.Local));
b.Property<int>("NotifyOnLevelUp");
b.Property<ulong>("UserId");
b.Property<int>("Xp");
b.HasKey("Id");
b.HasIndex("UserId", "GuildId")
.IsUnique();
b.ToTable("UserXpStats");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.VcRoleInfo", b =>
{
b.Property<int>("Id")
@ -1212,6 +1454,28 @@ namespace NadekoBot.Migrations
b.ToTable("WaifuInfo");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.WaifuItem", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<DateTime?>("DateAdded");
b.Property<int>("Item");
b.Property<string>("ItemEmoji");
b.Property<int>("Price");
b.Property<int?>("WaifuInfoId");
b.HasKey("Id");
b.HasIndex("WaifuInfoId");
b.ToTable("WaifuItem");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.WaifuUpdate", b =>
{
b.Property<int>("Id")
@ -1284,6 +1548,50 @@ namespace NadekoBot.Migrations
b.ToTable("WarningPunishment");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.XpRoleReward", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<DateTime?>("DateAdded");
b.Property<int>("Level");
b.Property<ulong>("RoleId");
b.Property<int>("XpSettingsId");
b.HasKey("Id");
b.HasIndex("XpSettingsId", "Level")
.IsUnique();
b.ToTable("XpRoleReward");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.XpSettings", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<DateTime?>("DateAdded");
b.Property<int>("GuildConfigId");
b.Property<string>("NotifyMessage");
b.Property<bool>("ServerExcluded");
b.Property<bool>("XpRoleRewardExclusive");
b.HasKey("Id");
b.HasIndex("GuildConfigId")
.IsUnique();
b.ToTable("XpSettings");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiRaidSetting", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig", "GuildConfig")
@ -1333,6 +1641,40 @@ namespace NadekoBot.Migrations
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.ClubApplicants", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.ClubInfo", "Club")
.WithMany("Applicants")
.HasForeignKey("ClubId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("NadekoBot.Services.Database.Models.DiscordUser", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.ClubBans", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.ClubInfo", "Club")
.WithMany("Bans")
.HasForeignKey("ClubId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("NadekoBot.Services.Database.Models.DiscordUser", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.ClubInfo", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.DiscordUser", "Owner")
.WithOne()
.HasForeignKey("NadekoBot.Services.Database.Models.ClubInfo", "OwnerId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandAlias", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig")
@ -1354,6 +1696,13 @@ namespace NadekoBot.Migrations
.HasForeignKey("BotConfigId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.DiscordUser", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.ClubInfo", "Club")
.WithMany("Users")
.HasForeignKey("ClubId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.EightBallResponse", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.BotConfig")
@ -1361,6 +1710,21 @@ namespace NadekoBot.Migrations
.HasForeignKey("BotConfigId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.ExcludedItem", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.XpSettings")
.WithMany("ExclusionList")
.HasForeignKey("XpSettingsId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.FeedSub", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig", "GuildConfig")
.WithMany("FeedSubs")
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterChannelId", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig")
@ -1439,6 +1803,13 @@ namespace NadekoBot.Migrations
.HasForeignKey("GuildConfigId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.NsfwBlacklitedTag", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig")
.WithMany("NsfwBlacklistedTags")
.HasForeignKey("GuildConfigId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.Permission", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.Permission", "Next")
@ -1510,6 +1881,28 @@ namespace NadekoBot.Migrations
.HasForeignKey("BotConfigId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.StreamRoleBlacklistedUser", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.StreamRoleSettings")
.WithMany("Blacklist")
.HasForeignKey("StreamRoleSettingsId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.StreamRoleSettings", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig", "GuildConfig")
.WithOne("StreamRole")
.HasForeignKey("NadekoBot.Services.Database.Models.StreamRoleSettings", "GuildConfigId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.StreamRoleWhitelistedUser", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.StreamRoleSettings")
.WithMany("Whitelist")
.HasForeignKey("StreamRoleSettingsId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.UnmuteTimer", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig")
@ -1540,6 +1933,13 @@ namespace NadekoBot.Migrations
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.WaifuItem", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.WaifuInfo")
.WithMany("Items")
.HasForeignKey("WaifuInfoId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.WaifuUpdate", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.DiscordUser", "New")
@ -1562,6 +1962,23 @@ namespace NadekoBot.Migrations
.WithMany("WarnPunishments")
.HasForeignKey("GuildConfigId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.XpRoleReward", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.XpSettings", "XpSettings")
.WithMany("RoleRewards")
.HasForeignKey("XpSettingsId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.XpSettings", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig", "GuildConfig")
.WithOne("XpSettings")
.HasForeignKey("NadekoBot.Services.Database.Models.XpSettings", "GuildConfigId")
.OnDelete(DeleteBehavior.Cascade);
});
#pragma warning restore 612, 618
}
}
}

View File

@ -5,23 +5,21 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using NadekoBot.Common.Attributes;
using NadekoBot.Services;
using NadekoBot.Attributes;
using NadekoBot.Modules.Administration.Services;
using NadekoBot.Services.Database.Models;
using NadekoBot.Services.Administration;
namespace NadekoBot.Modules.Administration
{
public partial class Administration : NadekoTopLevelModule
public partial class Administration : NadekoTopLevelModule<AdministrationService>
{
private IGuild _nadekoSupportServer;
private readonly DbService _db;
private readonly AdministrationService _admin;
public Administration(DbService db, AdministrationService admin)
public Administration(DbService db)
{
_db = db;
_admin = admin;
}
[NadekoCommand, Usage, Description, Aliases]
@ -40,12 +38,12 @@ namespace NadekoBot.Modules.Administration
}
if (enabled)
{
_admin.DeleteMessagesOnCommand.Add(Context.Guild.Id);
_service.DeleteMessagesOnCommand.Add(Context.Guild.Id);
await ReplyConfirmLocalized("delmsg_on").ConfigureAwait(false);
}
else
{
_admin.DeleteMessagesOnCommand.TryRemove(Context.Guild.Id);
_service.DeleteMessagesOnCommand.TryRemove(Context.Guild.Id);
await ReplyConfirmLocalized("delmsg_off").ConfigureAwait(false);
}
}
@ -58,7 +56,7 @@ namespace NadekoBot.Modules.Administration
{
var guser = (IGuildUser)Context.User;
var maxRole = guser.GetRoles().Max(x => x.Position);
if ((Context.User.Id != Context.Guild.OwnerId) && (maxRole < role.Position || maxRole <= usr.GetRoles().Max(x => x.Position)))
if ((Context.User.Id != Context.Guild.OwnerId) && (maxRole <= role.Position || maxRole <= usr.GetRoles().Max(x => x.Position)))
return;
try
{
@ -126,16 +124,15 @@ namespace NadekoBot.Modules.Administration
{
var guser = (IGuildUser)Context.User;
var userRoles = user.GetRoles();
if (guser.Id != Context.Guild.OwnerId &&
(user.Id == Context.Guild.OwnerId || guser.GetRoles().Max(x => x.Position) <= userRoles.Max(x => x.Position)))
var userRoles = user.GetRoles().Except(new[] { guser.Guild.EveryoneRole });
if (user.Id == Context.Guild.OwnerId || (Context.User.Id != Context.Guild.OwnerId && guser.GetRoles().Max(x => x.Position) <= userRoles.Max(x => x.Position)))
return;
try
{
await user.RemoveRolesAsync(userRoles).ConfigureAwait(false);
await ReplyConfirmLocalized("rar", Format.Bold(user.ToString())).ConfigureAwait(false);
}
catch
catch (Exception)
{
await ReplyErrorLocalized("rar_err").ConfigureAwait(false);
}

View File

@ -1,26 +1,24 @@
using Discord;
using Discord.Commands;
using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Services;
using NadekoBot.Services.Administration;
using System.Linq;
using System.Threading.Tasks;
using NadekoBot.Common.Attributes;
using NadekoBot.Modules.Administration.Services;
namespace NadekoBot.Modules.Administration
{
public partial class Administration
{
[Group]
public class AutoAssignRoleCommands : NadekoSubmodule
public class AutoAssignRoleCommands : NadekoSubmodule<AutoAssignRoleService>
{
private readonly DbService _db;
private readonly AutoAssignRoleService _service;
public AutoAssignRoleCommands(AutoAssignRoleService service, DbService db)
public AutoAssignRoleCommands(DbService db)
{
_db = db;
_service = service;
}
[NadekoCommand, Usage, Description, Aliases]
@ -39,7 +37,7 @@ namespace NadekoBot.Modules.Administration
if (role == null)
{
conf.AutoAssignRoleId = 0;
_service.AutoAssignedRoles.TryRemove(Context.Guild.Id, out ulong throwaway);
_service.AutoAssignedRoles.TryRemove(Context.Guild.Id, out _);
}
else
{

View File

@ -2,7 +2,7 @@
using System.Collections.Generic;
using System.Linq;
namespace NadekoBot.Modules.Administration.Commands.Migration
namespace NadekoBot.Modules.Administration.Common.Migration
{
public class CommandPrefixes0_9
{

View File

@ -1,6 +1,6 @@
using System;
namespace NadekoBot.Modules.Administration.Commands.Migration
namespace NadekoBot.Modules.Administration.Common.Migration
{
public class MigrationException : Exception
{

View File

@ -1,8 +1,9 @@
using Discord;
using System.Collections.Concurrent;
using Discord;
using NadekoBot.Common.Collections;
using NadekoBot.Services.Database.Models;
using System.Collections.Concurrent;
namespace NadekoBot.Services.Administration
namespace NadekoBot.Modules.Administration.Common
{
public enum ProtectionType
{

View File

@ -1,12 +1,13 @@
using Discord.WebSocket;
using System;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Discord.WebSocket;
using NadekoBot.Modules.Administration.Services;
namespace NadekoBot.Services.Administration
namespace NadekoBot.Modules.Administration.Common
{
public class Ratelimiter
{

View File

@ -1,10 +1,10 @@
using Discord;
using System;
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading;
using Discord;
namespace NadekoBot.Services.Administration
namespace NadekoBot.Modules.Administration.Common
{
public class UserSpamStats : IDisposable
{

View File

@ -1,24 +1,22 @@
using Discord;
using Discord.Commands;
using NadekoBot.Attributes;
using NadekoBot.Services;
using System.Threading.Tasks;
using NadekoBot.Services.Administration;
using NadekoBot.Common.Attributes;
using NadekoBot.Modules.Administration.Services;
namespace NadekoBot.Modules.Administration
{
public partial class Administration
{
[Group]
public class GameChannelCommands : NadekoSubmodule
public class GameChannelCommands : NadekoSubmodule<GameVoiceChannelService>
{
private readonly DbService _db;
private readonly GameVoiceChannelService _service;
public GameChannelCommands(GameVoiceChannelService service, DbService db)
public GameChannelCommands(DbService db)
{
_db = db;
_service = service;
}
[NadekoCommand, Usage, Description, Aliases]

View File

@ -1,6 +1,5 @@
using Discord;
using Discord.Commands;
using NadekoBot.Attributes;
using NadekoBot.Extensions;
using System;
using System.Collections.Generic;
@ -8,6 +7,7 @@ using System.Collections.Immutable;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
using NadekoBot.Common.Attributes;
namespace NadekoBot.Modules.Administration
{

View File

@ -1,16 +1,16 @@
using Discord;
using Discord.Commands;
using NadekoBot.Attributes;
using NadekoBot.DataStructures;
using NadekoBot.Extensions;
using NadekoBot.Modules.Permissions;
using NadekoBot.Services;
using NadekoBot.Services.Administration;
using NadekoBot.Services.Database.Models;
using System;
using System.Linq;
using System.Threading.Tasks;
using static NadekoBot.Services.Administration.LogCommandService;
using NadekoBot.Common;
using NadekoBot.Common.Attributes;
using NadekoBot.Common.TypeReaders.Models;
using NadekoBot.Modules.Administration.Services;
using static NadekoBot.Modules.Administration.Services.LogCommandService;
namespace NadekoBot.Modules.Administration
{
@ -18,14 +18,12 @@ namespace NadekoBot.Modules.Administration
{
[Group]
[NoPublicBot]
public class LogCommands : NadekoSubmodule
public class LogCommands : NadekoSubmodule<LogCommandService>
{
private readonly LogCommandService _lc;
private readonly DbService _db;
public LogCommands(LogCommandService lc, DbService db)
public LogCommands(DbService db)
{
_lc = lc;
_db = db;
}
@ -46,7 +44,7 @@ namespace NadekoBot.Modules.Administration
using (var uow = _db.UnitOfWork)
{
logSetting = uow.GuildConfigs.LogSettingsFor(channel.Guild.Id).LogSetting;
_lc.GuildLogSettings.AddOrUpdate(channel.Guild.Id, (id) => logSetting, (id, old) => logSetting);
_service.GuildLogSettings.AddOrUpdate(channel.Guild.Id, (id) => logSetting, (id, old) => logSetting);
logSetting.LogOtherId =
logSetting.MessageUpdatedId =
logSetting.MessageDeletedId =
@ -82,7 +80,7 @@ namespace NadekoBot.Modules.Administration
using (var uow = _db.UnitOfWork)
{
var config = uow.GuildConfigs.LogSettingsFor(channel.Guild.Id);
LogSetting logSetting = _lc.GuildLogSettings.GetOrAdd(channel.Guild.Id, (id) => config.LogSetting);
LogSetting logSetting = _service.GuildLogSettings.GetOrAdd(channel.Guild.Id, (id) => config.LogSetting);
removed = logSetting.IgnoredChannels.RemoveWhere(ilc => ilc.ChannelId == channel.Id);
config.LogSetting.IgnoredChannels.RemoveWhere(ilc => ilc.ChannelId == channel.Id);
if (removed == 0)
@ -106,8 +104,8 @@ namespace NadekoBot.Modules.Administration
[OwnerOnly]
public async Task LogEvents()
{
await Context.Channel.SendConfirmAsync(GetText("log_events") + "\n" +
string.Join(", ", Enum.GetNames(typeof(LogType)).Cast<string>()))
await Context.Channel.SendConfirmAsync(Format.Bold(GetText("log_events")) + "\n" +
$"```fix\n{string.Join(", ", Enum.GetNames(typeof(LogType)).Cast<string>())}```")
.ConfigureAwait(false);
}
@ -122,7 +120,7 @@ namespace NadekoBot.Modules.Administration
using (var uow = _db.UnitOfWork)
{
var logSetting = uow.GuildConfigs.LogSettingsFor(channel.Guild.Id).LogSetting;
_lc.GuildLogSettings.AddOrUpdate(channel.Guild.Id, (id) => logSetting, (id, old) => logSetting);
_service.GuildLogSettings.AddOrUpdate(channel.Guild.Id, (id) => logSetting, (id, old) => logSetting);
switch (type)
{
case LogType.Other:

View File

@ -4,27 +4,28 @@ using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Discord.Commands;
using NadekoBot.Attributes;
using NadekoBot.Services;
using NadekoBot.Services.Database.Models;
using Newtonsoft.Json;
using NadekoBot.Modules.Administration.Commands.Migration;
using System.Collections.Concurrent;
using NadekoBot.Extensions;
using NadekoBot.Services.Database;
using Microsoft.Data.Sqlite;
using NadekoBot.Common.Attributes;
using NadekoBot.Common.Collections;
using NadekoBot.Modules.Administration.Common.Migration;
namespace NadekoBot.Modules.Administration
{
public partial class Administration
{
[Group]
public class Migration : NadekoSubmodule
public class MigrationCommands : NadekoSubmodule
{
private const int CURRENT_VERSION = 1;
private readonly DbService _db;
public Migration(DbService db)
public MigrationCommands(DbService db)
{
_db = db;
}
@ -211,10 +212,10 @@ namespace NadekoBot.Modules.Administration
type = FollowedStream.FollowedStreamType.Twitch;
break;
case StreamNotificationConfig0_9.StreamType.Beam:
type = FollowedStream.FollowedStreamType.Beam;
type = FollowedStream.FollowedStreamType.Mixer;
break;
case StreamNotificationConfig0_9.StreamType.Hitbox:
type = FollowedStream.FollowedStreamType.Hitbox;
type = FollowedStream.FollowedStreamType.Smashcast;
break;
default:
break;
@ -351,54 +352,6 @@ namespace NadekoBot.Modules.Administration
oldConfig.RotatingStatuses.ForEach(i => messages.Add(new PlayingStatus { Status = i }));
botConfig.RotatingStatusMessages = messages;
//Prefix
botConfig.ModulePrefixes.Clear();
botConfig.ModulePrefixes.AddRange(new HashSet<ModulePrefix>
{
new ModulePrefix()
{
ModuleName = "Administration",
Prefix = oldConfig.CommandPrefixes.Administration
},
new ModulePrefix()
{
ModuleName = "Searches",
Prefix = oldConfig.CommandPrefixes.Searches
},
new ModulePrefix() {ModuleName = "NSFW", Prefix = oldConfig.CommandPrefixes.NSFW},
new ModulePrefix()
{
ModuleName = "Conversations",
Prefix = oldConfig.CommandPrefixes.Conversations
},
new ModulePrefix()
{
ModuleName = "ClashOfClans",
Prefix = oldConfig.CommandPrefixes.ClashOfClans
},
new ModulePrefix() {ModuleName = "Help", Prefix = oldConfig.CommandPrefixes.Help},
new ModulePrefix() {ModuleName = "Music", Prefix = oldConfig.CommandPrefixes.Music},
new ModulePrefix() {ModuleName = "Trello", Prefix = oldConfig.CommandPrefixes.Trello},
new ModulePrefix() {ModuleName = "Games", Prefix = oldConfig.CommandPrefixes.Games},
new ModulePrefix()
{
ModuleName = "Gambling",
Prefix = oldConfig.CommandPrefixes.Gambling
},
new ModulePrefix()
{
ModuleName = "Permissions",
Prefix = oldConfig.CommandPrefixes.Permissions
},
new ModulePrefix()
{
ModuleName = "Programming",
Prefix = oldConfig.CommandPrefixes.Programming
},
new ModulePrefix() {ModuleName = "Pokemon", Prefix = oldConfig.CommandPrefixes.Pokemon},
new ModulePrefix() {ModuleName = "Utility", Prefix = oldConfig.CommandPrefixes.Utility}
});
//Blacklist
var blacklist = new HashSet<BlacklistItem>(oldConfig.ServerBlacklist.Select(server => new BlacklistItem() { ItemId = server, Type = BlacklistType.Server }));
blacklist.AddRange(oldConfig.ChannelBlacklist.Select(channel => new BlacklistItem() { ItemId = channel, Type = BlacklistType.Channel }));

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