Merged dev into b2
This commit is contained in:
commit
dd4b04dbe1
@ -1 +0,0 @@
|
|||||||
Subproject commit afe768601f4e1322f34915d5c162acf47d0f6350
|
|
@ -33,8 +33,8 @@ Global
|
|||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
{45EC1473-C678-4857-A544-07DFE0D0B478}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{45EC1473-C678-4857-A544-07DFE0D0B478}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{45EC1473-C678-4857-A544-07DFE0D0B478}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{45EC1473-C678-4857-A544-07DFE0D0B478}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{45EC1473-C678-4857-A544-07DFE0D0B478}.GlobalNadeko|Any CPU.ActiveCfg = GlobalNadeko|Any CPU
|
{45EC1473-C678-4857-A544-07DFE0D0B478}.GlobalNadeko|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{45EC1473-C678-4857-A544-07DFE0D0B478}.GlobalNadeko|Any CPU.Build.0 = GlobalNadeko|Any CPU
|
{45EC1473-C678-4857-A544-07DFE0D0B478}.GlobalNadeko|Any CPU.Build.0 = Release|Any CPU
|
||||||
{45EC1473-C678-4857-A544-07DFE0D0B478}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{45EC1473-C678-4857-A544-07DFE0D0B478}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{45EC1473-C678-4857-A544-07DFE0D0B478}.Release|Any CPU.Build.0 = Release|Any CPU
|
{45EC1473-C678-4857-A544-07DFE0D0B478}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
@ -57,10 +57,10 @@ Command and aliases | Description | Usage
|
|||||||
`.lcsc` | Leaves Cross server channel instance from this channel. **Requires ManageServer server permission.** | `.lcsc`
|
`.lcsc` | Leaves Cross server channel instance from this channel. **Requires ManageServer server permission.** | `.lcsc`
|
||||||
`.fwmsgs` | Toggles forwarding of non-command messages sent to bot's DM to the bot owners **Bot Owner only.** | `.fwmsgs`
|
`.fwmsgs` | Toggles forwarding of non-command messages sent to bot's DM to the bot owners **Bot Owner only.** | `.fwmsgs`
|
||||||
`.fwtoall` | Toggles whether messages will be forwarded to all bot owners or only to the first one specified in the credentials.json **Bot Owner only.** | `.fwtoall`
|
`.fwtoall` | Toggles whether messages will be forwarded to all bot owners or only to the first one specified in the credentials.json **Bot Owner only.** | `.fwtoall`
|
||||||
`.logserver` | Logs server activity in this channel. **Requires Administrator server permission.** **Bot Owner only.** | `.logserver`
|
`.logserver` | Enables or Disables ALL log events. If enabled, all log events will log to this channel. **Requires Administrator server permission.** **Bot Owner only.** | `.logserver enable` or `.logserver disable`
|
||||||
`.logignore` | Toggles whether the .logserver command ignores this channel. Useful if you have hidden admin channel and public log channel. **Requires Administrator server permission.** **Bot Owner only.** | `.logignore`
|
`.logignore` | Toggles whether the .logserver command ignores this channel. Useful if you have hidden admin channel and public log channel. **Requires Administrator server permission.** **Bot Owner only.** | `.logignore`
|
||||||
`.userpresence` | Starts logging to this channel when someone from the server goes online/offline/idle. **Requires Administrator server permission.** | `.userpresence`
|
`.logevents` | Shows a list of all events you can subscribe to with `.log` **Requires Administrator server permission.** **Bot Owner only.** | `.logevents`
|
||||||
`.voicepresence` | Toggles logging to this channel whenever someone joins or leaves a voice channel you are currently in. **Requires Administrator server permission.** | `.voicepresence`
|
`.log` | Toggles logging event. Disables it if it's active anywhere on the server. Enables if it's not active. Use `.logevents` to see a list of all events you can subscribe to. **Requires Administrator server permission.** **Bot Owner only.** | `.log userpresence` or `.log userbanned`
|
||||||
`.repeatinvoke` `.repinv` | Immediately shows the repeat message and restarts the timer. **Requires ManageMessages server permission.** | `.repinv`
|
`.repeatinvoke` `.repinv` | Immediately shows the repeat message and restarts the timer. **Requires ManageMessages server permission.** | `.repinv`
|
||||||
`.repeat` | Repeat a message every X minutes. If no parameters are specified, repeat is disabled. **Requires ManageMessages server permission.** | `.repeat 5 Hello there`
|
`.repeat` | Repeat a message every X minutes. If no parameters are specified, repeat is disabled. **Requires ManageMessages server permission.** | `.repeat 5 Hello there`
|
||||||
`.migratedata` | Migrate data from old bot configuration **Bot Owner only.** | `.migratedata`
|
`.migratedata` | Migrate data from old bot configuration **Bot Owner only.** | `.migratedata`
|
||||||
@ -120,6 +120,8 @@ Command and aliases | Description | Usage
|
|||||||
`.listcustreactg` `.lcrg` | Lists global or server custom reactions (20 commands per page) grouped by trigger, and show a number of responses for each. Running the command in DM will list global custom reactions, while running it in server will list that server's custom reactions. | `.lcrg 1`
|
`.listcustreactg` `.lcrg` | Lists global or server custom reactions (20 commands per page) grouped by trigger, and show a number of responses for each. Running the command in DM will list global custom reactions, while running it in server will list that server's custom reactions. | `.lcrg 1`
|
||||||
`.showcustreact` `.scr` | Shows a custom reaction's response on a given ID. | `.scr 1`
|
`.showcustreact` `.scr` | Shows a custom reaction's response on a given ID. | `.scr 1`
|
||||||
`.delcustreact` `.dcr` | Deletes a custom reaction on a specific index. If ran in DM, it is bot owner only and deletes a global custom reaction. If ran in a server, it requires Administration priviledges and removes server custom reaction. | `.dcr 5`
|
`.delcustreact` `.dcr` | Deletes a custom reaction on a specific index. If ran in DM, it is bot owner only and deletes a global custom reaction. If ran in a server, it requires Administration priviledges and removes server custom reaction. | `.dcr 5`
|
||||||
|
`.crstatsclear` | Resets the counters on `.crstats`. You can specify a trigger to clear stats only for that trigger. | `.crstatsclear` or `.crstatsclear rng`
|
||||||
|
`.crstats` | Shows a list of custom reactions and the number of times they have been executed. Paginated with 10 per page. Use `.crstatsclear` to reset the counters. | `.crstats` or `.crstats 3`
|
||||||
|
|
||||||
###### [Back to TOC](#table-of-contents)
|
###### [Back to TOC](#table-of-contents)
|
||||||
|
|
||||||
@ -135,7 +137,7 @@ Command and aliases | Description | Usage
|
|||||||
`$leaderboard` `$lb` | Displays bot currency leaderboard. | `$lb`
|
`$leaderboard` `$lb` | Displays bot currency leaderboard. | `$lb`
|
||||||
`$race` | Starts a new animal race. | `$race`
|
`$race` | Starts a new animal race. | `$race`
|
||||||
`$joinrace` `$jr` | Joins a new race. You can specify an amount of currency for betting (optional). You will get YourBet*(participants-1) back if you win. | `$jr` or `$jr 5`
|
`$joinrace` `$jr` | Joins a new race. You can specify an amount of currency for betting (optional). You will get YourBet*(participants-1) back if you win. | `$jr` or `$jr 5`
|
||||||
`$roll` | Rolls 0-100. If you supply a number [x] it rolls up to 30 normal dice. If you split 2 numbers with letter d (xdy) it will roll x dice from 1 to y. | `$roll` or `$roll 7` or `$roll 3d5`
|
`$roll` | Rolls 0-100. If you supply a number [x] it rolls up to 30 normal dice. If you split 2 numbers with letter d (xdy) it will roll x dice from 1 to y. Y can be a letter 'F' if you want to roll fate dice instead of dnd. | `$roll` or `$roll 7` or `$roll 3d5` or `$roll 5dF`
|
||||||
`$rolluo` | Rolls X normal dice (up to 30) unordered. If you split 2 numbers with letter d (xdy) it will roll x dice from 1 to y. | `$rolluo` or `$rolluo 7` or `$rolluo 3d5`
|
`$rolluo` | Rolls X normal dice (up to 30) unordered. If you split 2 numbers with letter d (xdy) it will roll x dice from 1 to y. | `$rolluo` or `$rolluo 7` or `$rolluo 3d5`
|
||||||
`$nroll` | Rolls in a given range. | `$nroll 5` (rolls 0-5) or `$nroll 5-15`
|
`$nroll` | Rolls in a given range. | `$nroll 5` (rolls 0-5) or `$nroll 5-15`
|
||||||
`$draw` | Draws a card from the deck.If you supply number X, she draws up to 5 cards from the deck. | `$draw` or `$draw 5`
|
`$draw` | Draws a card from the deck.If you supply number X, she draws up to 5 cards from the deck. | `$draw` or `$draw 5`
|
||||||
@ -156,8 +158,11 @@ Command and aliases | Description | Usage
|
|||||||
`>poll` | Creates a poll which requires users to send the number of the voting option to the bot. **Requires ManageMessages server permission.** | `>poll Question?;Answer1;Answ 2;A_3`
|
`>poll` | Creates a poll which requires users to send the number of the voting option to the bot. **Requires ManageMessages server permission.** | `>poll Question?;Answer1;Answ 2;A_3`
|
||||||
`>publicpoll` `>ppoll` | Creates a public poll which requires users to type a number of the voting option in the channel command is ran in. **Requires ManageMessages server permission.** | `>ppoll Question?;Answer1;Answ 2;A_3`
|
`>publicpoll` `>ppoll` | Creates a public poll which requires users to type a number of the voting option in the channel command is ran in. **Requires ManageMessages server permission.** | `>ppoll Question?;Answer1;Answ 2;A_3`
|
||||||
`>pollend` | Stops active poll on this server and prints the results in this channel. **Requires ManageMessages server permission.** | `>pollend`
|
`>pollend` | Stops active poll on this server and prints the results in this channel. **Requires ManageMessages server permission.** | `>pollend`
|
||||||
|
`>acrophobia` `>acro` | Starts an Acrophobia game. Second argment is optional round length in seconds. (default is 60) | `>acro` or `>acro 30`
|
||||||
`>cleverbot` | Toggles cleverbot session. When enabled, the bot will reply to messages starting with bot mention in the server. Custom reactions starting with %mention% won't work if cleverbot is enabled. **Requires ManageMessages server permission.** | `>cleverbot`
|
`>cleverbot` | Toggles cleverbot session. When enabled, the bot will reply to messages starting with bot mention in the server. Custom reactions starting with %mention% won't work if cleverbot is enabled. **Requires ManageMessages server permission.** | `>cleverbot`
|
||||||
`>pick` | Picks the currency planted in this channel. | `>pick`
|
`>hangmanlist` | Shows a list of hangman term types. | `> hangmanlist`
|
||||||
|
`>hangman` | Starts a game of hangman in the channel. Use `>hangmanlist` to see a list of available term types. Defaults to 'all'. | `>hangman` or `>hangman movies`
|
||||||
|
`>pick` | Picks the currency planted in this channel. 60 seconds cooldown. | `>pick`
|
||||||
`>plant` | Spend a unit of currency to plant it in this channel. (If bot is restarted or crashes, the currency will be lost) | `>plant`
|
`>plant` | Spend a unit of currency to plant it in this channel. (If bot is restarted or crashes, the currency will be lost) | `>plant`
|
||||||
`>gencurrency` `>gc` | Toggles currency generation on this channel. Every posted message will have chance to spawn currency. Chance is specified by the Bot Owner. (default is 2%) **Requires ManageMessages server permission.** | `>gc`
|
`>gencurrency` `>gc` | Toggles currency generation on this channel. Every posted message will have chance to spawn currency. Chance is specified by the Bot Owner. (default is 2%) **Requires ManageMessages server permission.** | `>gc`
|
||||||
`>typestart` | Starts a typing contest. | `>typestart`
|
`>typestart` | Starts a typing contest. | `>typestart`
|
||||||
@ -190,6 +195,7 @@ Command and aliases | Description | Usage
|
|||||||
`!!stop` `!!s` | Stops the music and clears the playlist. Stays in the channel. | `!!s`
|
`!!stop` `!!s` | Stops the music and clears the playlist. Stays in the channel. | `!!s`
|
||||||
`!!destroy` `!!d` | Completely stops the music and unbinds the bot from the channel. (may cause weird behaviour) | `!!d`
|
`!!destroy` `!!d` | Completely stops the music and unbinds the bot from the channel. (may cause weird behaviour) | `!!d`
|
||||||
`!!pause` `!!p` | Pauses or Unpauses the song. | `!!p`
|
`!!pause` `!!p` | Pauses or Unpauses the song. | `!!p`
|
||||||
|
`!!fairplay` `!!fp` | Toggles fairplay. While enabled, music player will prioritize songs from users who didn't have their song recently played instead of the song's position in the queue. | `!!fp`
|
||||||
`!!queue` `!!q` `!!yq` | Queue a song using keywords or a link. Bot will join your voice channel.**You must be in a voice channel**. | `!!q Dream Of Venice`
|
`!!queue` `!!q` `!!yq` | Queue a song using keywords or a link. Bot will join your voice channel.**You must be in a voice channel**. | `!!q Dream Of Venice`
|
||||||
`!!soundcloudqueue` `!!sq` | Queue a soundcloud song using keywords. Bot will join your voice channel.**You must be in a voice channel**. | `!!sq Dream Of Venice`
|
`!!soundcloudqueue` `!!sq` | Queue a soundcloud song using keywords. Bot will join your voice channel.**You must be in a voice channel**. | `!!sq Dream Of Venice`
|
||||||
`!!listqueue` `!!lq` | Lists 15 currently queued songs per page. Default page is 1. | `!!lq` or `!!lq 2`
|
`!!listqueue` `!!lq` | Lists 15 currently queued songs per page. Default page is 1. | `!!lq` or `!!lq 2`
|
||||||
@ -206,6 +212,7 @@ Command and aliases | Description | Usage
|
|||||||
`!!remove` `!!rm` | Remove a song by its # in the queue, or 'all' to remove whole queue. | `!!rm 5`
|
`!!remove` `!!rm` | Remove a song by its # in the queue, or 'all' to remove whole queue. | `!!rm 5`
|
||||||
`!!movesong` `!!ms` | Moves a song from one position to another. | `!!ms 5>3`
|
`!!movesong` `!!ms` | Moves a song from one position to another. | `!!ms 5>3`
|
||||||
`!!setmaxqueue` `!!smq` | Sets a maximum queue size. Supply 0 or no argument to have no limit. | `!!smq 50` or `!!smq`
|
`!!setmaxqueue` `!!smq` | Sets a maximum queue size. Supply 0 or no argument to have no limit. | `!!smq 50` or `!!smq`
|
||||||
|
`!!setmaxplaytime` `!!smp` | Sets a maximum number of seconds (>14) a song can run before being skipped automatically. Set 0 to have no limit. | `!!smp 0` or `!!smp 270`
|
||||||
`!!reptcursong` `!!rcs` | Toggles repeat of current song. | `!!rcs`
|
`!!reptcursong` `!!rcs` | Toggles repeat of current song. | `!!rcs`
|
||||||
`!!rpeatplaylst` `!!rpl` | Toggles repeat of all songs in the queue (every song that finishes is added to the end of the queue). | `!!rpl`
|
`!!rpeatplaylst` `!!rpl` | Toggles repeat of all songs in the queue (every song that finishes is added to the end of the queue). | `!!rpl`
|
||||||
`!!save` | Saves a playlist under a certain name. Name must be no longer than 20 characters and mustn't contain dashes. | `!!save classical1`
|
`!!save` | Saves a playlist under a certain name. Name must be no longer than 20 characters and mustn't contain dashes. | `!!save classical1`
|
||||||
@ -213,7 +220,6 @@ Command and aliases | Description | Usage
|
|||||||
`!!playlists` `!!pls` | Lists all playlists. Paginated. 20 per page. Default page is 0. | `!!pls 1`
|
`!!playlists` `!!pls` | Lists all playlists. Paginated. 20 per page. Default page is 0. | `!!pls 1`
|
||||||
`!!deleteplaylist` `!!delpls` | Deletes a saved playlist. Only if you made it or if you are the bot owner. | `!!delpls animu-5`
|
`!!deleteplaylist` `!!delpls` | Deletes a saved playlist. Only if you made it or if you are the bot owner. | `!!delpls animu-5`
|
||||||
`!!goto` | Goes to a specific time in seconds in a song. | `!!goto 30`
|
`!!goto` | Goes to a specific time in seconds in a song. | `!!goto 30`
|
||||||
`!!getlink` `!!gl` | Shows a link to the song in the queue by index, or the currently playing song by default. | `!!gl`
|
|
||||||
`!!autoplay` `!!ap` | Toggles autoplay - When the song is finished, automatically queue a related youtube song. (Works only for youtube songs and when queue is empty) | `!!ap`
|
`!!autoplay` `!!ap` | Toggles autoplay - When the song is finished, automatically queue a related youtube song. (Works only for youtube songs and when queue is empty) | `!!ap`
|
||||||
|
|
||||||
###### [Back to TOC](#table-of-contents)
|
###### [Back to TOC](#table-of-contents)
|
||||||
@ -222,9 +228,10 @@ Command and aliases | Description | Usage
|
|||||||
Command and aliases | Description | Usage
|
Command and aliases | Description | Usage
|
||||||
----------------|--------------|-------
|
----------------|--------------|-------
|
||||||
`~hentai` | Shows a hentai image from a random website (gelbooru or danbooru or konachan or atfbooru or yandere) with a given tag. Tag is optional but preferred. Only 1 tag allowed. | `~hentai yuri`
|
`~hentai` | Shows a hentai image from a random website (gelbooru or danbooru or konachan or atfbooru or yandere) with a given tag. Tag is optional but preferred. Only 1 tag allowed. | `~hentai yuri`
|
||||||
|
`~autohentai` | Posts a hentai every X seconds with a random tag from the provided tags. Use `|` to separate tags. 20 seconds minimum. Provide no arguments to disable. | `~autohentai 30 yuri|tail|long_hair` or `~autohentai`
|
||||||
`~hentaibomb` | Shows a total 5 images (from gelbooru, danbooru, konachan, yandere and atfbooru). Tag is optional but preferred. | `~hentaibomb yuri`
|
`~hentaibomb` | Shows a total 5 images (from gelbooru, danbooru, konachan, yandere and atfbooru). Tag is optional but preferred. | `~hentaibomb yuri`
|
||||||
`~yandere` | Shows a random image from yandere with a given tag. Tag is optional but preferred. (multiple tags are appended with +) | `~yandere tag1+tag2`
|
|
||||||
`~danbooru` | Shows a random hentai image from danbooru with a given tag. Tag is optional but preferred. (multiple tags are appended with +) | `~danbooru yuri+kissing`
|
`~danbooru` | Shows a random hentai image from danbooru with a given tag. Tag is optional but preferred. (multiple tags are appended with +) | `~danbooru yuri+kissing`
|
||||||
|
`~yandere` | Shows a random image from yandere with a given tag. Tag is optional but preferred. (multiple tags are appended with +) | `~yandere tag1+tag2`
|
||||||
`~konachan` | Shows a random hentai image from konachan with a given tag. Tag is optional but preferred. | `~konachan yuri`
|
`~konachan` | Shows a random hentai image from konachan with a given tag. Tag is optional but preferred. | `~konachan yuri`
|
||||||
`~gelbooru` | Shows a random hentai image from gelbooru with a given tag. Tag is optional but preferred. (multiple tags are appended with +) | `~gelbooru yuri+kissing`
|
`~gelbooru` | Shows a random hentai image from gelbooru with a given tag. Tag is optional but preferred. (multiple tags are appended with +) | `~gelbooru yuri+kissing`
|
||||||
`~rule34` | Shows a random image from rule34.xx with a given tag. Tag is optional but preferred. (multiple tags are appended with +) | `~rule34 yuri+kissing`
|
`~rule34` | Shows a random image from rule34.xx with a given tag. Tag is optional but preferred. (multiple tags are appended with +) | `~rule34 yuri+kissing`
|
||||||
@ -244,13 +251,13 @@ Command and aliases | Description | Usage
|
|||||||
`;removeperm` `;rp` | Removes a permission from a given position in Permissions list. | `;rp 1`
|
`;removeperm` `;rp` | Removes a permission from a given position in Permissions list. | `;rp 1`
|
||||||
`;moveperm` `;mp` | Moves permission from one position to another in Permissions list. | `;mp 2 4`
|
`;moveperm` `;mp` | Moves permission from one position to another in Permissions list. | `;mp 2 4`
|
||||||
`;srvrcmd` `;sc` | Sets a command's permission at the server level. | `;sc "command name" disable`
|
`;srvrcmd` `;sc` | Sets a command's permission at the server level. | `;sc "command name" disable`
|
||||||
`;srvrmdl` `;sm` | Sets a module's permission at the server level. | `;sm "module name" enable`
|
`;srvrmdl` `;sm` | Sets a module's permission at the server level. | `;sm ModuleName enable`
|
||||||
`;usrcmd` `;uc` | Sets a command's permission at the user level. | `;uc "command name" enable SomeUsername`
|
`;usrcmd` `;uc` | Sets a command's permission at the user level. | `;uc "command name" enable SomeUsername`
|
||||||
`;usrmdl` `;um` | Sets a module's permission at the user level. | `;um "module name" enable SomeUsername`
|
`;usrmdl` `;um` | Sets a module's permission at the user level. | `;um ModuleName enable SomeUsername`
|
||||||
`;rolecmd` `;rc` | Sets a command's permission at the role level. | `;rc "command name" disable MyRole`
|
`;rolecmd` `;rc` | Sets a command's permission at the role level. | `;rc "command name" disable MyRole`
|
||||||
`;rolemdl` `;rm` | Sets a module's permission at the role level. | `;rm "module name" enable MyRole`
|
`;rolemdl` `;rm` | Sets a module's permission at the role level. | `;rm ModuleName enable MyRole`
|
||||||
`;chnlcmd` `;cc` | Sets a command's permission at the channel level. | `;cc "command name" enable SomeChannel`
|
`;chnlcmd` `;cc` | Sets a command's permission at the channel level. | `;cc "command name" enable SomeChannel`
|
||||||
`;chnlmdl` `;cm` | Sets a module's permission at the channel level. | `;cm "module name" enable SomeChannel`
|
`;chnlmdl` `;cm` | Sets a module's permission at the channel level. | `;cm ModuleName enable SomeChannel`
|
||||||
`;allchnlmdls` `;acm` | Enable or disable all modules in a specified channel. | `;acm enable #SomeChannel`
|
`;allchnlmdls` `;acm` | Enable or disable all modules in a specified channel. | `;acm enable #SomeChannel`
|
||||||
`;allrolemdls` `;arm` | Enable or disable all modules for a specific role. | `;arm [enable/disable] MyRole`
|
`;allrolemdls` `;arm` | Enable or disable all modules for a specific role. | `;arm [enable/disable] MyRole`
|
||||||
`;allusrmdls` `;aum` | Enable or disable all modules for a specific user. | `;aum enable @someone`
|
`;allusrmdls` `;aum` | Enable or disable all modules for a specific user. | `;aum enable @someone`
|
||||||
@ -283,7 +290,7 @@ Command and aliases | Description | Usage
|
|||||||
### Searches
|
### Searches
|
||||||
Command and aliases | Description | Usage
|
Command and aliases | Description | Usage
|
||||||
----------------|--------------|-------
|
----------------|--------------|-------
|
||||||
`~weather` `~we` | Shows weather data for a specified city and a country. BOTH ARE REQUIRED. Use country abbrevations. | `~we Moscow RF`
|
`~weather` `~we` | Shows weather data for a specified city. You can also specify a country after a comma. | `~we Moscow, RU`
|
||||||
`~youtube` `~yt` | Searches youtubes and shows the first result | `~yt query`
|
`~youtube` `~yt` | Searches youtubes and shows the first result | `~yt query`
|
||||||
`~imdb` `~omdb` | Queries omdb for movies or series, show first result. | `~imdb Batman vs Superman`
|
`~imdb` `~omdb` | Queries omdb for movies or series, show first result. | `~imdb Batman vs Superman`
|
||||||
`~randomcat` `~meow` | Shows a random cat image. | `~meow`
|
`~randomcat` `~meow` | Shows a random cat image. | `~meow`
|
||||||
@ -297,6 +304,7 @@ Command and aliases | Description | Usage
|
|||||||
`~hearthstone` `~hs` | Searches for a Hearthstone card and shows its image. Takes a while to complete. | `~hs Ysera`
|
`~hearthstone` `~hs` | Searches for a Hearthstone card and shows its image. Takes a while to complete. | `~hs Ysera`
|
||||||
`~yodify` `~yoda` | Translates your normal sentences into Yoda styled sentences! | ~yodify I was once an adventurer like you` or `~yoda my feelings hurt`
|
`~yodify` `~yoda` | Translates your normal sentences into Yoda styled sentences! | ~yodify I was once an adventurer like you` or `~yoda my feelings hurt`
|
||||||
`~urbandict` `~ud` | Searches Urban Dictionary for a word. | `~ud Pineapple`
|
`~urbandict` `~ud` | Searches Urban Dictionary for a word. | `~ud Pineapple`
|
||||||
|
`~define` `~def` | Finds a definition of a word. | `~def heresy`
|
||||||
`~#` | Searches Tagdef.com for a hashtag. | `~# ff`
|
`~#` | Searches Tagdef.com for a hashtag. | `~# ff`
|
||||||
`~catfact` | Shows a random catfact from <http://catfacts-api.appspot.com/api/facts> | `~catfact`
|
`~catfact` | Shows a random catfact from <http://catfacts-api.appspot.com/api/facts> | `~catfact`
|
||||||
`~revav` | Returns a google reverse image search for someone's avatar. | `~revav "@SomeGuy"`
|
`~revav` | Returns a google reverse image search for someone's avatar. | `~revav "@SomeGuy"`
|
||||||
@ -322,6 +330,7 @@ Command and aliases | Description | Usage
|
|||||||
`~osu` | Shows osu stats for a player. | `~osu Name` or `~osu Name taiko`
|
`~osu` | Shows osu stats for a player. | `~osu Name` or `~osu Name taiko`
|
||||||
`~osub` | Shows information about an osu beatmap. | `~osub https://osu.ppy.sh/s/127712`
|
`~osub` | Shows information about an osu beatmap. | `~osub https://osu.ppy.sh/s/127712`
|
||||||
`~osu5` | Displays a user's top 5 plays. | `~osu5 Name`
|
`~osu5` | Displays a user's top 5 plays. | `~osu5 Name`
|
||||||
|
`~overwatch` `~ow` | Show's basic stats on a player (competitive rank, playtime, level etc) Region codes are: `eu` `us` `cn` `kr` | `~ow us Battletag#1337` or `~overwatch eu Battletag#2016`
|
||||||
`~placelist` | Shows the list of available tags for the `~place` command. | `~placelist`
|
`~placelist` | Shows the list of available tags for the `~place` command. | `~placelist`
|
||||||
`~place` | Shows a placeholder image of a given tag. Use `~placelist` to see all available tags. You can specify the width and height of the image as the last two optional arguments. | `~place Cage` or `~place steven 500 400`
|
`~place` | Shows a placeholder image of a given tag. Use `~placelist` to see all available tags. You can specify the width and height of the image as the last two optional arguments. | `~place Cage` or `~place steven 500 400`
|
||||||
`~pokemon` `~poke` | Searches for a pokemon. | `~poke Sylveon`
|
`~pokemon` `~poke` | Searches for a pokemon. | `~poke Sylveon`
|
||||||
@ -343,6 +352,7 @@ Command and aliases | Description | Usage
|
|||||||
### Utility
|
### Utility
|
||||||
Command and aliases | Description | Usage
|
Command and aliases | Description | Usage
|
||||||
----------------|--------------|-------
|
----------------|--------------|-------
|
||||||
|
`.togethertube` `.totube` | Creates a new room on <https://togethertube.com> and shows the link in the chat. | `.totube`
|
||||||
`.whosplaying` `.whpl` | Shows a list of users who are playing the specified game. | `.whpl Overwatch`
|
`.whosplaying` `.whpl` | Shows a list of users who are playing the specified game. | `.whpl Overwatch`
|
||||||
`.inrole` | Lists every person from the provided role or roles (separated by a ',') on this server. If the list is too long for 1 message, you must have Manage Messages permission. | `.inrole Role`
|
`.inrole` | Lists every person from the provided role or roles (separated by a ',') on this server. If the list is too long for 1 message, you must have Manage Messages permission. | `.inrole Role`
|
||||||
`.checkmyperms` | Checks your user-specific permissions on this channel. | `.checkmyperms`
|
`.checkmyperms` | Checks your user-specific permissions on this channel. | `.checkmyperms`
|
||||||
@ -359,6 +369,7 @@ Command and aliases | Description | Usage
|
|||||||
`.serverinfo` `.sinfo` | Shows info about the server the bot is on. If no channel is supplied, it defaults to current one. | `.sinfo Some Server`
|
`.serverinfo` `.sinfo` | Shows info about the server the bot is on. If no channel is supplied, it defaults to current one. | `.sinfo Some Server`
|
||||||
`.channelinfo` `.cinfo` | Shows info about the channel. If no channel is supplied, it defaults to current one. | `.cinfo #some-channel`
|
`.channelinfo` `.cinfo` | Shows info about the channel. If no channel is supplied, it defaults to current one. | `.cinfo #some-channel`
|
||||||
`.userinfo` `.uinfo` | Shows info about the user. If no user is supplied, it defaults a user running the command. | `.uinfo @SomeUser`
|
`.userinfo` `.uinfo` | Shows info about the user. If no user is supplied, it defaults a user running the command. | `.uinfo @SomeUser`
|
||||||
|
`.activity` | Checks for spammers. **Bot Owner only.** | `.activity`
|
||||||
`.listquotes` `.liqu` | `.liqu` or `.liqu 3` | Lists all quotes on the server ordered alphabetically. 15 Per page.
|
`.listquotes` `.liqu` | `.liqu` or `.liqu 3` | Lists all quotes on the server ordered alphabetically. 15 Per page.
|
||||||
`...` | Shows a random quote with a specified name. | `... abc`
|
`...` | Shows a random quote with a specified name. | `... abc`
|
||||||
`..` | Adds a new quote with the specified name and message. | `.. sayhi Hi`
|
`..` | Adds a new quote with the specified name and message. | `.. sayhi Hi`
|
||||||
|
855
src/NadekoBot/Migrations/20161224032833_logsettings.Designer.cs
generated
Normal file
855
src/NadekoBot/Migrations/20161224032833_logsettings.Designer.cs
generated
Normal file
@ -0,0 +1,855 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using NadekoBot.Services.Database;
|
||||||
|
using NadekoBot.Services.Database.Models;
|
||||||
|
using NadekoBot.Modules.Music.Classes;
|
||||||
|
|
||||||
|
namespace NadekoBot.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(NadekoContext))]
|
||||||
|
[Migration("20161224032833_logsettings")]
|
||||||
|
partial class logsettings
|
||||||
|
{
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "1.1.0-rtm-22752");
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.BlacklistItem", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int?>("BotConfigId");
|
||||||
|
|
||||||
|
b.Property<ulong>("ItemId");
|
||||||
|
|
||||||
|
b.Property<int>("Type");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("BotConfigId");
|
||||||
|
|
||||||
|
b.ToTable("BlacklistItem");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.BotConfig", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<ulong>("BufferSize");
|
||||||
|
|
||||||
|
b.Property<float>("CurrencyGenerationChance");
|
||||||
|
|
||||||
|
b.Property<int>("CurrencyGenerationCooldown");
|
||||||
|
|
||||||
|
b.Property<string>("CurrencyName");
|
||||||
|
|
||||||
|
b.Property<string>("CurrencyPluralName");
|
||||||
|
|
||||||
|
b.Property<string>("CurrencySign");
|
||||||
|
|
||||||
|
b.Property<string>("DMHelpString");
|
||||||
|
|
||||||
|
b.Property<bool>("ForwardMessages");
|
||||||
|
|
||||||
|
b.Property<bool>("ForwardToAllOwners");
|
||||||
|
|
||||||
|
b.Property<string>("HelpString");
|
||||||
|
|
||||||
|
b.Property<int>("MigrationVersion");
|
||||||
|
|
||||||
|
b.Property<string>("RemindMessageFormat");
|
||||||
|
|
||||||
|
b.Property<bool>("RotatingStatuses");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("BotConfig");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.ClashCaller", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<bool>("BaseDestroyed");
|
||||||
|
|
||||||
|
b.Property<string>("CallUser");
|
||||||
|
|
||||||
|
b.Property<int>("ClashWarId");
|
||||||
|
|
||||||
|
b.Property<int?>("SequenceNumber");
|
||||||
|
|
||||||
|
b.Property<int>("Stars");
|
||||||
|
|
||||||
|
b.Property<DateTime>("TimeAdded");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ClashWarId");
|
||||||
|
|
||||||
|
b.ToTable("ClashCallers");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.ClashWar", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<ulong>("ChannelId");
|
||||||
|
|
||||||
|
b.Property<string>("EnemyClan");
|
||||||
|
|
||||||
|
b.Property<ulong>("GuildId");
|
||||||
|
|
||||||
|
b.Property<int>("Size");
|
||||||
|
|
||||||
|
b.Property<DateTime>("StartedAt");
|
||||||
|
|
||||||
|
b.Property<int>("WarState");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("ClashOfClans");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandCooldown", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("CommandName");
|
||||||
|
|
||||||
|
b.Property<int?>("GuildConfigId");
|
||||||
|
|
||||||
|
b.Property<int>("Seconds");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("GuildConfigId");
|
||||||
|
|
||||||
|
b.ToTable("CommandCooldown");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.ConvertUnit", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("InternalTrigger");
|
||||||
|
|
||||||
|
b.Property<decimal>("Modifier");
|
||||||
|
|
||||||
|
b.Property<string>("UnitType");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("ConversionUnits");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.Currency", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<long>("Amount");
|
||||||
|
|
||||||
|
b.Property<ulong>("UserId");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("Currency");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.CurrencyTransaction", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<long>("Amount");
|
||||||
|
|
||||||
|
b.Property<string>("Reason");
|
||||||
|
|
||||||
|
b.Property<ulong>("UserId");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("CurrencyTransactions");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.CustomReaction", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<ulong?>("GuildId");
|
||||||
|
|
||||||
|
b.Property<bool>("IsRegex");
|
||||||
|
|
||||||
|
b.Property<bool>("OwnerOnly");
|
||||||
|
|
||||||
|
b.Property<string>("Response");
|
||||||
|
|
||||||
|
b.Property<string>("Trigger");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("CustomReactions");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.Donator", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int>("Amount");
|
||||||
|
|
||||||
|
b.Property<string>("Name");
|
||||||
|
|
||||||
|
b.Property<ulong>("UserId");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("Donators");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.EightBallResponse", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int?>("BotConfigId");
|
||||||
|
|
||||||
|
b.Property<string>("Text");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("BotConfigId");
|
||||||
|
|
||||||
|
b.ToTable("EightBallResponses");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterChannelId", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<ulong>("ChannelId");
|
||||||
|
|
||||||
|
b.Property<int?>("GuildConfigId");
|
||||||
|
|
||||||
|
b.Property<int?>("GuildConfigId1");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("GuildConfigId");
|
||||||
|
|
||||||
|
b.HasIndex("GuildConfigId1");
|
||||||
|
|
||||||
|
b.ToTable("FilterChannelId");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.FilteredWord", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int?>("GuildConfigId");
|
||||||
|
|
||||||
|
b.Property<string>("Word");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("GuildConfigId");
|
||||||
|
|
||||||
|
b.ToTable("FilteredWord");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.FollowedStream", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<ulong>("ChannelId");
|
||||||
|
|
||||||
|
b.Property<int?>("GuildConfigId");
|
||||||
|
|
||||||
|
b.Property<ulong>("GuildId");
|
||||||
|
|
||||||
|
b.Property<int>("Type");
|
||||||
|
|
||||||
|
b.Property<string>("Username");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("GuildConfigId");
|
||||||
|
|
||||||
|
b.ToTable("FollowedStream");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.GCChannelId", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<ulong>("ChannelId");
|
||||||
|
|
||||||
|
b.Property<int?>("GuildConfigId");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("GuildConfigId");
|
||||||
|
|
||||||
|
b.ToTable("GCChannelId");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.GuildConfig", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<ulong>("AutoAssignRoleId");
|
||||||
|
|
||||||
|
b.Property<bool>("AutoDeleteByeMessages");
|
||||||
|
|
||||||
|
b.Property<int>("AutoDeleteByeMessagesTimer");
|
||||||
|
|
||||||
|
b.Property<bool>("AutoDeleteGreetMessages");
|
||||||
|
|
||||||
|
b.Property<int>("AutoDeleteGreetMessagesTimer");
|
||||||
|
|
||||||
|
b.Property<bool>("AutoDeleteSelfAssignedRoleMessages");
|
||||||
|
|
||||||
|
b.Property<ulong>("ByeMessageChannelId");
|
||||||
|
|
||||||
|
b.Property<string>("ChannelByeMessageText");
|
||||||
|
|
||||||
|
b.Property<string>("ChannelGreetMessageText");
|
||||||
|
|
||||||
|
b.Property<bool>("CleverbotEnabled");
|
||||||
|
|
||||||
|
b.Property<float>("DefaultMusicVolume");
|
||||||
|
|
||||||
|
b.Property<bool>("DeleteMessageOnCommand");
|
||||||
|
|
||||||
|
b.Property<string>("DmGreetMessageText");
|
||||||
|
|
||||||
|
b.Property<bool>("ExclusiveSelfAssignedRoles");
|
||||||
|
|
||||||
|
b.Property<bool>("FilterInvites");
|
||||||
|
|
||||||
|
b.Property<bool>("FilterWords");
|
||||||
|
|
||||||
|
b.Property<ulong>("GreetMessageChannelId");
|
||||||
|
|
||||||
|
b.Property<ulong>("GuildId");
|
||||||
|
|
||||||
|
b.Property<int?>("LogSettingId");
|
||||||
|
|
||||||
|
b.Property<string>("MuteRoleName");
|
||||||
|
|
||||||
|
b.Property<string>("PermissionRole");
|
||||||
|
|
||||||
|
b.Property<int?>("RootPermissionId");
|
||||||
|
|
||||||
|
b.Property<bool>("SendChannelByeMessage");
|
||||||
|
|
||||||
|
b.Property<bool>("SendChannelGreetMessage");
|
||||||
|
|
||||||
|
b.Property<bool>("SendDmGreetMessage");
|
||||||
|
|
||||||
|
b.Property<bool>("VerbosePermissions");
|
||||||
|
|
||||||
|
b.Property<bool>("VoicePlusTextEnabled");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("GuildId")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.HasIndex("LogSettingId");
|
||||||
|
|
||||||
|
b.HasIndex("RootPermissionId");
|
||||||
|
|
||||||
|
b.ToTable("GuildConfigs");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredLogChannel", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<ulong>("ChannelId");
|
||||||
|
|
||||||
|
b.Property<int?>("LogSettingId");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("LogSettingId");
|
||||||
|
|
||||||
|
b.ToTable("IgnoredLogChannels");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredVoicePresenceChannel", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<ulong>("ChannelId");
|
||||||
|
|
||||||
|
b.Property<int?>("LogSettingId");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("LogSettingId");
|
||||||
|
|
||||||
|
b.ToTable("IgnoredVoicePresenceCHannels");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.LogSetting", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<bool>("ChannelCreated");
|
||||||
|
|
||||||
|
b.Property<ulong?>("ChannelCreatedId");
|
||||||
|
|
||||||
|
b.Property<bool>("ChannelDestroyed");
|
||||||
|
|
||||||
|
b.Property<ulong?>("ChannelDestroyedId");
|
||||||
|
|
||||||
|
b.Property<ulong>("ChannelId");
|
||||||
|
|
||||||
|
b.Property<bool>("ChannelUpdated");
|
||||||
|
|
||||||
|
b.Property<ulong?>("ChannelUpdatedId");
|
||||||
|
|
||||||
|
b.Property<bool>("IsLogging");
|
||||||
|
|
||||||
|
b.Property<ulong?>("LogOtherId");
|
||||||
|
|
||||||
|
b.Property<bool>("LogUserPresence");
|
||||||
|
|
||||||
|
b.Property<ulong?>("LogUserPresenceId");
|
||||||
|
|
||||||
|
b.Property<bool>("LogVoicePresence");
|
||||||
|
|
||||||
|
b.Property<ulong?>("LogVoicePresenceId");
|
||||||
|
|
||||||
|
b.Property<ulong?>("LogVoicePresenceTTSId");
|
||||||
|
|
||||||
|
b.Property<bool>("MessageDeleted");
|
||||||
|
|
||||||
|
b.Property<ulong?>("MessageDeletedId");
|
||||||
|
|
||||||
|
b.Property<bool>("MessageUpdated");
|
||||||
|
|
||||||
|
b.Property<ulong?>("MessageUpdatedId");
|
||||||
|
|
||||||
|
b.Property<bool>("UserBanned");
|
||||||
|
|
||||||
|
b.Property<ulong?>("UserBannedId");
|
||||||
|
|
||||||
|
b.Property<bool>("UserJoined");
|
||||||
|
|
||||||
|
b.Property<ulong?>("UserJoinedId");
|
||||||
|
|
||||||
|
b.Property<bool>("UserLeft");
|
||||||
|
|
||||||
|
b.Property<ulong?>("UserLeftId");
|
||||||
|
|
||||||
|
b.Property<ulong?>("UserMutedId");
|
||||||
|
|
||||||
|
b.Property<ulong>("UserPresenceChannelId");
|
||||||
|
|
||||||
|
b.Property<bool>("UserUnbanned");
|
||||||
|
|
||||||
|
b.Property<ulong?>("UserUnbannedId");
|
||||||
|
|
||||||
|
b.Property<bool>("UserUpdated");
|
||||||
|
|
||||||
|
b.Property<ulong?>("UserUpdatedId");
|
||||||
|
|
||||||
|
b.Property<ulong>("VoicePresenceChannelId");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("LogSettings");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.ModulePrefix", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int?>("BotConfigId");
|
||||||
|
|
||||||
|
b.Property<string>("ModuleName");
|
||||||
|
|
||||||
|
b.Property<string>("Prefix");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("BotConfigId");
|
||||||
|
|
||||||
|
b.ToTable("ModulePrefixes");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.MusicPlaylist", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("Author");
|
||||||
|
|
||||||
|
b.Property<ulong>("AuthorId");
|
||||||
|
|
||||||
|
b.Property<string>("Name");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("MusicPlaylists");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.MutedUserId", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int?>("GuildConfigId");
|
||||||
|
|
||||||
|
b.Property<ulong>("UserId");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("GuildConfigId");
|
||||||
|
|
||||||
|
b.ToTable("MutedUserId");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.Permission", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int?>("NextId");
|
||||||
|
|
||||||
|
b.Property<int>("PrimaryTarget");
|
||||||
|
|
||||||
|
b.Property<ulong>("PrimaryTargetId");
|
||||||
|
|
||||||
|
b.Property<int>("SecondaryTarget");
|
||||||
|
|
||||||
|
b.Property<string>("SecondaryTargetName");
|
||||||
|
|
||||||
|
b.Property<bool>("State");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("NextId")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("Permission");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.PlayingStatus", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int?>("BotConfigId");
|
||||||
|
|
||||||
|
b.Property<string>("Status");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("BotConfigId");
|
||||||
|
|
||||||
|
b.ToTable("PlayingStatus");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.PlaylistSong", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int?>("MusicPlaylistId");
|
||||||
|
|
||||||
|
b.Property<string>("Provider");
|
||||||
|
|
||||||
|
b.Property<int>("ProviderType");
|
||||||
|
|
||||||
|
b.Property<string>("Query");
|
||||||
|
|
||||||
|
b.Property<string>("Title");
|
||||||
|
|
||||||
|
b.Property<string>("Uri");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("MusicPlaylistId");
|
||||||
|
|
||||||
|
b.ToTable("PlaylistSong");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.Quote", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<ulong>("AuthorId");
|
||||||
|
|
||||||
|
b.Property<string>("AuthorName")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Property<ulong>("GuildId");
|
||||||
|
|
||||||
|
b.Property<string>("Keyword")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Property<string>("Text")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Quotes");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.RaceAnimal", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int?>("BotConfigId");
|
||||||
|
|
||||||
|
b.Property<string>("Icon");
|
||||||
|
|
||||||
|
b.Property<string>("Name");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("BotConfigId");
|
||||||
|
|
||||||
|
b.ToTable("RaceAnimals");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.Reminder", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<ulong>("ChannelId");
|
||||||
|
|
||||||
|
b.Property<bool>("IsPrivate");
|
||||||
|
|
||||||
|
b.Property<string>("Message");
|
||||||
|
|
||||||
|
b.Property<ulong>("ServerId");
|
||||||
|
|
||||||
|
b.Property<ulong>("UserId");
|
||||||
|
|
||||||
|
b.Property<DateTime>("When");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Reminders");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.Repeater", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<ulong>("ChannelId");
|
||||||
|
|
||||||
|
b.Property<ulong>("GuildId");
|
||||||
|
|
||||||
|
b.Property<TimeSpan>("Interval");
|
||||||
|
|
||||||
|
b.Property<string>("Message");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ChannelId")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("Repeaters");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.SelfAssignedRole", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<ulong>("GuildId");
|
||||||
|
|
||||||
|
b.Property<ulong>("RoleId");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("GuildId", "RoleId")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("SelfAssignableRoles");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.UserPokeTypes", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<ulong>("UserId");
|
||||||
|
|
||||||
|
b.Property<string>("type");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("PokeGame");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.BlacklistItem", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("NadekoBot.Services.Database.Models.BotConfig")
|
||||||
|
.WithMany("Blacklist")
|
||||||
|
.HasForeignKey("BotConfigId");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.ClashCaller", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("NadekoBot.Services.Database.Models.ClashWar", "ClashWar")
|
||||||
|
.WithMany("Bases")
|
||||||
|
.HasForeignKey("ClashWarId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandCooldown", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig")
|
||||||
|
.WithMany("CommandCooldowns")
|
||||||
|
.HasForeignKey("GuildConfigId");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.EightBallResponse", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("NadekoBot.Services.Database.Models.BotConfig")
|
||||||
|
.WithMany("EightBallResponses")
|
||||||
|
.HasForeignKey("BotConfigId");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterChannelId", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig")
|
||||||
|
.WithMany("FilterInvitesChannelIds")
|
||||||
|
.HasForeignKey("GuildConfigId");
|
||||||
|
|
||||||
|
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig")
|
||||||
|
.WithMany("FilterWordsChannelIds")
|
||||||
|
.HasForeignKey("GuildConfigId1");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.FilteredWord", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig")
|
||||||
|
.WithMany("FilteredWords")
|
||||||
|
.HasForeignKey("GuildConfigId");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.FollowedStream", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig")
|
||||||
|
.WithMany("FollowedStreams")
|
||||||
|
.HasForeignKey("GuildConfigId");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.GCChannelId", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig")
|
||||||
|
.WithMany("GenerateCurrencyChannelIds")
|
||||||
|
.HasForeignKey("GuildConfigId");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.GuildConfig", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("NadekoBot.Services.Database.Models.LogSetting", "LogSetting")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("LogSettingId");
|
||||||
|
|
||||||
|
b.HasOne("NadekoBot.Services.Database.Models.Permission", "RootPermission")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RootPermissionId");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredLogChannel", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("NadekoBot.Services.Database.Models.LogSetting", "LogSetting")
|
||||||
|
.WithMany("IgnoredChannels")
|
||||||
|
.HasForeignKey("LogSettingId");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredVoicePresenceChannel", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("NadekoBot.Services.Database.Models.LogSetting", "LogSetting")
|
||||||
|
.WithMany("IgnoredVoicePresenceChannelIds")
|
||||||
|
.HasForeignKey("LogSettingId");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.ModulePrefix", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("NadekoBot.Services.Database.Models.BotConfig")
|
||||||
|
.WithMany("ModulePrefixes")
|
||||||
|
.HasForeignKey("BotConfigId");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.MutedUserId", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig")
|
||||||
|
.WithMany("MutedUsers")
|
||||||
|
.HasForeignKey("GuildConfigId");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.Permission", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("NadekoBot.Services.Database.Models.Permission", "Next")
|
||||||
|
.WithOne("Previous")
|
||||||
|
.HasForeignKey("NadekoBot.Services.Database.Models.Permission", "NextId");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.PlayingStatus", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("NadekoBot.Services.Database.Models.BotConfig")
|
||||||
|
.WithMany("RotatingStatusMessages")
|
||||||
|
.HasForeignKey("BotConfigId");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.PlaylistSong", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("NadekoBot.Services.Database.Models.MusicPlaylist")
|
||||||
|
.WithMany("Songs")
|
||||||
|
.HasForeignKey("MusicPlaylistId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Database.Models.RaceAnimal", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("NadekoBot.Services.Database.Models.BotConfig")
|
||||||
|
.WithMany("RaceAnimals")
|
||||||
|
.HasForeignKey("BotConfigId");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
150
src/NadekoBot/Migrations/20161224032833_logsettings.cs
Normal file
150
src/NadekoBot/Migrations/20161224032833_logsettings.cs
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
namespace NadekoBot.Migrations
|
||||||
|
{
|
||||||
|
public partial class logsettings : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<ulong>(
|
||||||
|
name: "ChannelCreatedId",
|
||||||
|
table: "LogSettings",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<ulong>(
|
||||||
|
name: "ChannelDestroyedId",
|
||||||
|
table: "LogSettings",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<ulong>(
|
||||||
|
name: "ChannelUpdatedId",
|
||||||
|
table: "LogSettings",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<ulong>(
|
||||||
|
name: "LogOtherId",
|
||||||
|
table: "LogSettings",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<ulong>(
|
||||||
|
name: "LogUserPresenceId",
|
||||||
|
table: "LogSettings",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<ulong>(
|
||||||
|
name: "LogVoicePresenceId",
|
||||||
|
table: "LogSettings",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<ulong>(
|
||||||
|
name: "LogVoicePresenceTTSId",
|
||||||
|
table: "LogSettings",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<ulong>(
|
||||||
|
name: "MessageDeletedId",
|
||||||
|
table: "LogSettings",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<ulong>(
|
||||||
|
name: "MessageUpdatedId",
|
||||||
|
table: "LogSettings",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<ulong>(
|
||||||
|
name: "UserBannedId",
|
||||||
|
table: "LogSettings",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<ulong>(
|
||||||
|
name: "UserJoinedId",
|
||||||
|
table: "LogSettings",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<ulong>(
|
||||||
|
name: "UserLeftId",
|
||||||
|
table: "LogSettings",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<ulong>(
|
||||||
|
name: "UserMutedId",
|
||||||
|
table: "LogSettings",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<ulong>(
|
||||||
|
name: "UserUnbannedId",
|
||||||
|
table: "LogSettings",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<ulong>(
|
||||||
|
name: "UserUpdatedId",
|
||||||
|
table: "LogSettings",
|
||||||
|
nullable: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "ChannelCreatedId",
|
||||||
|
table: "LogSettings");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "ChannelDestroyedId",
|
||||||
|
table: "LogSettings");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "ChannelUpdatedId",
|
||||||
|
table: "LogSettings");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "LogOtherId",
|
||||||
|
table: "LogSettings");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "LogUserPresenceId",
|
||||||
|
table: "LogSettings");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "LogVoicePresenceId",
|
||||||
|
table: "LogSettings");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "LogVoicePresenceTTSId",
|
||||||
|
table: "LogSettings");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "MessageDeletedId",
|
||||||
|
table: "LogSettings");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "MessageUpdatedId",
|
||||||
|
table: "LogSettings");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "UserBannedId",
|
||||||
|
table: "LogSettings");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "UserJoinedId",
|
||||||
|
table: "LogSettings");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "UserLeftId",
|
||||||
|
table: "LogSettings");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "UserMutedId",
|
||||||
|
table: "LogSettings");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "UserUnbannedId",
|
||||||
|
table: "LogSettings");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "UserUpdatedId",
|
||||||
|
table: "LogSettings");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -420,34 +420,64 @@ namespace NadekoBot.Migrations
|
|||||||
|
|
||||||
b.Property<bool>("ChannelCreated");
|
b.Property<bool>("ChannelCreated");
|
||||||
|
|
||||||
|
b.Property<ulong?>("ChannelCreatedId");
|
||||||
|
|
||||||
b.Property<bool>("ChannelDestroyed");
|
b.Property<bool>("ChannelDestroyed");
|
||||||
|
|
||||||
|
b.Property<ulong?>("ChannelDestroyedId");
|
||||||
|
|
||||||
b.Property<ulong>("ChannelId");
|
b.Property<ulong>("ChannelId");
|
||||||
|
|
||||||
b.Property<bool>("ChannelUpdated");
|
b.Property<bool>("ChannelUpdated");
|
||||||
|
|
||||||
|
b.Property<ulong?>("ChannelUpdatedId");
|
||||||
|
|
||||||
b.Property<bool>("IsLogging");
|
b.Property<bool>("IsLogging");
|
||||||
|
|
||||||
|
b.Property<ulong?>("LogOtherId");
|
||||||
|
|
||||||
b.Property<bool>("LogUserPresence");
|
b.Property<bool>("LogUserPresence");
|
||||||
|
|
||||||
|
b.Property<ulong?>("LogUserPresenceId");
|
||||||
|
|
||||||
b.Property<bool>("LogVoicePresence");
|
b.Property<bool>("LogVoicePresence");
|
||||||
|
|
||||||
|
b.Property<ulong?>("LogVoicePresenceId");
|
||||||
|
|
||||||
|
b.Property<ulong?>("LogVoicePresenceTTSId");
|
||||||
|
|
||||||
b.Property<bool>("MessageDeleted");
|
b.Property<bool>("MessageDeleted");
|
||||||
|
|
||||||
|
b.Property<ulong?>("MessageDeletedId");
|
||||||
|
|
||||||
b.Property<bool>("MessageUpdated");
|
b.Property<bool>("MessageUpdated");
|
||||||
|
|
||||||
|
b.Property<ulong?>("MessageUpdatedId");
|
||||||
|
|
||||||
b.Property<bool>("UserBanned");
|
b.Property<bool>("UserBanned");
|
||||||
|
|
||||||
|
b.Property<ulong?>("UserBannedId");
|
||||||
|
|
||||||
b.Property<bool>("UserJoined");
|
b.Property<bool>("UserJoined");
|
||||||
|
|
||||||
|
b.Property<ulong?>("UserJoinedId");
|
||||||
|
|
||||||
b.Property<bool>("UserLeft");
|
b.Property<bool>("UserLeft");
|
||||||
|
|
||||||
|
b.Property<ulong?>("UserLeftId");
|
||||||
|
|
||||||
|
b.Property<ulong?>("UserMutedId");
|
||||||
|
|
||||||
b.Property<ulong>("UserPresenceChannelId");
|
b.Property<ulong>("UserPresenceChannelId");
|
||||||
|
|
||||||
b.Property<bool>("UserUnbanned");
|
b.Property<bool>("UserUnbanned");
|
||||||
|
|
||||||
|
b.Property<ulong?>("UserUnbannedId");
|
||||||
|
|
||||||
b.Property<bool>("UserUpdated");
|
b.Property<bool>("UserUpdated");
|
||||||
|
|
||||||
|
b.Property<ulong?>("UserUpdatedId");
|
||||||
|
|
||||||
b.Property<ulong>("VoicePresenceChannelId");
|
b.Property<ulong>("VoicePresenceChannelId");
|
||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
@ -73,7 +73,8 @@ namespace NadekoBot.Modules.Administration
|
|||||||
PermRole = config.PermissionRole,
|
PermRole = config.PermissionRole,
|
||||||
Verbose = config.VerbosePermissions,
|
Verbose = config.VerbosePermissions,
|
||||||
};
|
};
|
||||||
Permissions.Permissions.Cache.AddOrUpdate(Context.Guild.Id, toAdd, (id, old) => toAdd);
|
Permissions.Permissions.Cache.AddOrUpdate(channel.Guild.Id,
|
||||||
|
toAdd, (id, old) => toAdd);
|
||||||
await uow.CompleteAsync();
|
await uow.CompleteAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -346,7 +347,7 @@ namespace NadekoBot.Modules.Administration
|
|||||||
{
|
{
|
||||||
foreach (var u in users)
|
foreach (var u in users)
|
||||||
{
|
{
|
||||||
await u.ModifyAsync(usr=>usr.Deaf = true).ConfigureAwait(false);
|
await u.ModifyAsync(usr => usr.Deaf = true).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
await Context.Channel.SendConfirmAsync("🔇 **Deafen** successful.").ConfigureAwait(false);
|
await Context.Channel.SendConfirmAsync("🔇 **Deafen** successful.").ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
@ -367,7 +368,7 @@ namespace NadekoBot.Modules.Administration
|
|||||||
{
|
{
|
||||||
foreach (var u in users)
|
foreach (var u in users)
|
||||||
{
|
{
|
||||||
await u.ModifyAsync(usr=> usr.Deaf = false).ConfigureAwait(false);
|
await u.ModifyAsync(usr => usr.Deaf = false).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
await Context.Channel.SendConfirmAsync("🔊 **Undeafen** successful.").ConfigureAwait(false);
|
await Context.Channel.SendConfirmAsync("🔊 **Undeafen** successful.").ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
@ -585,8 +586,9 @@ namespace NadekoBot.Modules.Administration
|
|||||||
var channels = await Task.WhenAll(NadekoBot.Client.GetGuilds().Select(g =>
|
var channels = await Task.WhenAll(NadekoBot.Client.GetGuilds().Select(g =>
|
||||||
g.GetDefaultChannelAsync()
|
g.GetDefaultChannelAsync()
|
||||||
)).ConfigureAwait(false);
|
)).ConfigureAwait(false);
|
||||||
|
if (channels == null)
|
||||||
await Task.WhenAll(channels.Select(c => c.SendConfirmAsync($"🆕 Message from {Context.User} `[Bot Owner]`:", message)))
|
return;
|
||||||
|
await Task.WhenAll(channels.Where(c => c != null).Select(c => c.SendConfirmAsync($"🆕 Message from {Context.User} `[Bot Owner]`:", message)))
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
await Context.Channel.SendConfirmAsync("🆗").ConfigureAwait(false);
|
await Context.Channel.SendConfirmAsync("🆗").ConfigureAwait(false);
|
||||||
@ -618,7 +620,7 @@ namespace NadekoBot.Modules.Administration
|
|||||||
}
|
}
|
||||||
var title = $"Chatlog-{Context.Guild.Name}/#{Context.Channel.Name}-{DateTime.Now}.txt";
|
var title = $"Chatlog-{Context.Guild.Name}/#{Context.Channel.Name}-{DateTime.Now}.txt";
|
||||||
await (Context.User as IGuildUser).SendFileAsync(
|
await (Context.User as IGuildUser).SendFileAsync(
|
||||||
await JsonConvert.SerializeObject(new { Messages = msgs.Select(s => $"【{s.Timestamp:HH:mm:ss}】{s.Author}:" + s.ToString()) }, Formatting.Indented).ToStream().ConfigureAwait(false),
|
await JsonConvert.SerializeObject(grouping, Formatting.Indented).ToStream().ConfigureAwait(false),
|
||||||
title, title).ConfigureAwait(false);
|
title, title).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,52 +81,46 @@ namespace NadekoBot.Modules.Administration
|
|||||||
{
|
{
|
||||||
_log = LogManager.GetCurrentClassLogger();
|
_log = LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
NadekoBot.Client.MessageReceived += (imsg) =>
|
NadekoBot.Client.MessageReceived += async (imsg) =>
|
||||||
{
|
{
|
||||||
var msg = imsg as IUserMessage;
|
|
||||||
if (msg == null || imsg.Author.IsBot)
|
|
||||||
return Task.CompletedTask;
|
|
||||||
|
|
||||||
var channel = imsg.Channel as ITextChannel;
|
try
|
||||||
if (channel == null)
|
|
||||||
return Task.CompletedTask;
|
|
||||||
|
|
||||||
var t = Task.Run(async () =>
|
|
||||||
{
|
{
|
||||||
try
|
var msg = imsg as IUserMessage;
|
||||||
|
if (msg == null || msg.Author.IsBot)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var channel = msg.Channel as ITextChannel;
|
||||||
|
if (channel == null)
|
||||||
|
return;
|
||||||
|
AntiSpamSetting spamSettings;
|
||||||
|
if (!antiSpamGuilds.TryGetValue(channel.Guild.Id, out spamSettings))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var stats = spamSettings.UserStats.AddOrUpdate(msg.Author.Id, new UserSpamStats(msg.Content),
|
||||||
|
(id, old) => { old.ApplyNextMessage(msg.Content); return old; });
|
||||||
|
|
||||||
|
if (stats.Count >= spamSettings.MessageThreshold)
|
||||||
{
|
{
|
||||||
AntiSpamSetting spamSettings;
|
if (spamSettings.UserStats.TryRemove(msg.Author.Id, out stats))
|
||||||
if (!antiSpamGuilds.TryGetValue(channel.Guild.Id, out spamSettings))
|
|
||||||
return;
|
|
||||||
|
|
||||||
var stats = spamSettings.UserStats.AddOrUpdate(imsg.Author.Id, new UserSpamStats(msg.Content),
|
|
||||||
(id, old) => { old.ApplyNextMessage(msg.Content); return old; });
|
|
||||||
|
|
||||||
if (stats.Count >= spamSettings.MessageThreshold)
|
|
||||||
{
|
{
|
||||||
if (spamSettings.UserStats.TryRemove(imsg.Author.Id, out stats))
|
await PunishUsers(spamSettings.Action, ProtectionType.Spamming, (IGuildUser)msg.Author)
|
||||||
{
|
.ConfigureAwait(false);
|
||||||
await PunishUsers(spamSettings.Action, ProtectionType.Spamming, (IGuildUser)imsg.Author)
|
|
||||||
.ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch { }
|
}
|
||||||
});
|
catch { }
|
||||||
return Task.CompletedTask;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
NadekoBot.Client.UserJoined += (usr) =>
|
NadekoBot.Client.UserJoined += async (usr) =>
|
||||||
{
|
{
|
||||||
if (usr.IsBot)
|
try
|
||||||
return Task.CompletedTask;
|
|
||||||
|
|
||||||
AntiRaidSetting settings;
|
|
||||||
if (!antiRaidGuilds.TryGetValue(usr.Guild.Id, out settings))
|
|
||||||
return Task.CompletedTask;
|
|
||||||
|
|
||||||
var t = Task.Run(async () =>
|
|
||||||
{
|
{
|
||||||
|
if (usr.IsBot)
|
||||||
|
return;
|
||||||
|
AntiRaidSetting settings;
|
||||||
|
if (!antiRaidGuilds.TryGetValue(usr.Guild.Id, out settings))
|
||||||
|
return;
|
||||||
if (!settings.RaidUsers.Add(usr))
|
if (!settings.RaidUsers.Add(usr))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -143,9 +137,9 @@ namespace NadekoBot.Modules.Administration
|
|||||||
|
|
||||||
settings.RaidUsers.TryRemove(usr);
|
settings.RaidUsers.TryRemove(usr);
|
||||||
--settings.UsersCount;
|
--settings.UsersCount;
|
||||||
});
|
|
||||||
|
|
||||||
return Task.CompletedTask;
|
}
|
||||||
|
catch { }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,8 @@ using NadekoBot.Services;
|
|||||||
using NadekoBot.Services.Database.Models;
|
using NadekoBot.Services.Database.Models;
|
||||||
using NLog;
|
using NLog;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
@ -17,30 +19,36 @@ namespace NadekoBot.Modules.Administration
|
|||||||
public class AutoAssignRoleCommands : ModuleBase
|
public class AutoAssignRoleCommands : ModuleBase
|
||||||
{
|
{
|
||||||
private static Logger _log { get; }
|
private static Logger _log { get; }
|
||||||
|
//guildid/roleid
|
||||||
|
private static ConcurrentDictionary<ulong, ulong> AutoAssignedRoles { get; }
|
||||||
|
|
||||||
static AutoAssignRoleCommands()
|
static AutoAssignRoleCommands()
|
||||||
{
|
{
|
||||||
_log = LogManager.GetCurrentClassLogger();
|
_log = LogManager.GetCurrentClassLogger();
|
||||||
NadekoBot.Client.UserJoined += (user) =>
|
var sw = Stopwatch.StartNew();
|
||||||
|
|
||||||
|
AutoAssignedRoles = new ConcurrentDictionary<ulong, ulong>(NadekoBot.AllGuildConfigs.Where(x => x.AutoAssignRoleId != 0)
|
||||||
|
.ToDictionary(k => k.GuildId, v => v.AutoAssignRoleId));
|
||||||
|
NadekoBot.Client.UserJoined += async (user) =>
|
||||||
{
|
{
|
||||||
var t = Task.Run(async () =>
|
try
|
||||||
{
|
{
|
||||||
try
|
ulong roleId = 0;
|
||||||
{
|
AutoAssignedRoles.TryGetValue(user.Guild.Id, out roleId);
|
||||||
GuildConfig conf = NadekoBot.AllGuildConfigs.FirstOrDefault(gc => gc.GuildId == user.Guild.Id);
|
|
||||||
|
|
||||||
if (conf.AutoAssignRoleId == 0)
|
if (roleId == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var role = user.Guild.Roles.FirstOrDefault(r => r.Id == conf.AutoAssignRoleId);
|
var role = user.Guild.Roles.FirstOrDefault(r => r.Id == roleId);
|
||||||
|
|
||||||
if (role != null)
|
if (role != null)
|
||||||
await user.AddRolesAsync(role);
|
await user.AddRolesAsync(role).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (Exception ex) { _log.Warn(ex); }
|
catch (Exception ex) { _log.Warn(ex); }
|
||||||
});
|
|
||||||
return Task.CompletedTask;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
sw.Stop();
|
||||||
|
_log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s");
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
@ -53,9 +61,16 @@ namespace NadekoBot.Modules.Administration
|
|||||||
{
|
{
|
||||||
conf = uow.GuildConfigs.For(Context.Guild.Id, set => set);
|
conf = uow.GuildConfigs.For(Context.Guild.Id, set => set);
|
||||||
if (role == null)
|
if (role == null)
|
||||||
|
{
|
||||||
conf.AutoAssignRoleId = 0;
|
conf.AutoAssignRoleId = 0;
|
||||||
|
ulong throwaway;
|
||||||
|
AutoAssignedRoles.TryRemove(channel.Guild.Id, out throwaway);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
conf.AutoAssignRoleId = role.Id;
|
conf.AutoAssignRoleId = role.Id;
|
||||||
|
AutoAssignedRoles.AddOrUpdate(channel.Guild.Id, role.Id, (key, val) => role.Id);
|
||||||
|
}
|
||||||
|
|
||||||
await uow.CompleteAsync().ConfigureAwait(false);
|
await uow.CompleteAsync().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
@ -19,22 +19,19 @@ namespace NadekoBot.Modules.Administration
|
|||||||
static CrossServerTextChannel()
|
static CrossServerTextChannel()
|
||||||
{
|
{
|
||||||
_log = LogManager.GetCurrentClassLogger();
|
_log = LogManager.GetCurrentClassLogger();
|
||||||
NadekoBot.Client.MessageReceived += (imsg) =>
|
NadekoBot.Client.MessageReceived += async (imsg) =>
|
||||||
{
|
{
|
||||||
if (imsg.Author.IsBot)
|
try
|
||||||
return Task.CompletedTask;
|
|
||||||
|
|
||||||
var msg = imsg as IUserMessage;
|
|
||||||
if (msg == null)
|
|
||||||
return Task.CompletedTask;
|
|
||||||
|
|
||||||
var channel = imsg.Channel as ITextChannel;
|
|
||||||
if (channel == null)
|
|
||||||
return Task.CompletedTask;
|
|
||||||
|
|
||||||
Task.Run(async () =>
|
|
||||||
{
|
{
|
||||||
if (imsg.Author.Id == NadekoBot.Client.CurrentUser().Id) return;
|
if (imsg.Author.IsBot)
|
||||||
|
return;
|
||||||
|
var msg = imsg as IUserMessage;
|
||||||
|
if (msg == null)
|
||||||
|
return;
|
||||||
|
var channel = imsg.Channel as ITextChannel;
|
||||||
|
if (channel == null)
|
||||||
|
return;
|
||||||
|
if (msg.Author.Id == NadekoBot.Client.GetCurrentUser().Id) return;
|
||||||
foreach (var subscriber in Subscribers)
|
foreach (var subscriber in Subscribers)
|
||||||
{
|
{
|
||||||
var set = subscriber.Value;
|
var set = subscriber.Value;
|
||||||
@ -45,8 +42,10 @@ namespace NadekoBot.Modules.Administration
|
|||||||
try { await chan.SendMessageAsync(GetText(channel.Guild, channel, (IGuildUser)msg.Author, msg)).ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
|
try { await chan.SendMessageAsync(GetText(channel.Guild, channel, (IGuildUser)msg.Author, msg)).ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
return Task.CompletedTask;
|
catch (Exception ex) {
|
||||||
|
_log.Warn(ex);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,9 @@ using Discord.WebSocket;
|
|||||||
using NadekoBot.Attributes;
|
using NadekoBot.Attributes;
|
||||||
using NadekoBot.Extensions;
|
using NadekoBot.Extensions;
|
||||||
using NadekoBot.Services;
|
using NadekoBot.Services;
|
||||||
|
using NLog;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
@ -18,8 +20,12 @@ namespace NadekoBot.Modules.Administration
|
|||||||
private static bool ForwardDMs { get; set; }
|
private static bool ForwardDMs { get; set; }
|
||||||
private static bool ForwardDMsToAllOwners { get; set; }
|
private static bool ForwardDMsToAllOwners { get; set; }
|
||||||
|
|
||||||
|
private static readonly Logger _log;
|
||||||
|
|
||||||
static DMForwardCommands()
|
static DMForwardCommands()
|
||||||
{
|
{
|
||||||
|
_log = LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
using (var uow = DbHandler.UnitOfWork())
|
using (var uow = DbHandler.UnitOfWork())
|
||||||
{
|
{
|
||||||
var config = uow.BotConfig.GetOrCreate();
|
var config = uow.BotConfig.GetOrCreate();
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -7,6 +7,7 @@ using NadekoBot.Services.Database.Models;
|
|||||||
using NLog;
|
using NLog;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@ -49,10 +50,16 @@ namespace NadekoBot.Modules.Administration
|
|||||||
{
|
{
|
||||||
while (!token.IsCancellationRequested)
|
while (!token.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
|
var toSend = "🔄 " + Repeater.Message;
|
||||||
await Task.Delay(Repeater.Interval, token).ConfigureAwait(false);
|
await Task.Delay(Repeater.Interval, token).ConfigureAwait(false);
|
||||||
|
|
||||||
|
//var lastMsgInChannel = (await Channel.GetMessagesAsync(2)).FirstOrDefault();
|
||||||
|
// if (lastMsgInChannel.Id == oldMsg?.Id) //don't send if it's the same message in the channel
|
||||||
|
// continue;
|
||||||
|
|
||||||
if (oldMsg != null)
|
if (oldMsg != null)
|
||||||
try { await oldMsg.DeleteAsync(); } catch { }
|
try { await oldMsg.DeleteAsync(); } catch { }
|
||||||
try { oldMsg = await Channel.SendMessageAsync("🔄 " + Repeater.Message).ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); try { source.Cancel(); } catch { } }
|
try { oldMsg = await Channel.SendMessageAsync(toSend).ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException) { }
|
catch (OperationCanceledException) { }
|
||||||
@ -72,10 +79,15 @@ namespace NadekoBot.Modules.Administration
|
|||||||
|
|
||||||
static RepeatCommands()
|
static RepeatCommands()
|
||||||
{
|
{
|
||||||
|
var _log = LogManager.GetCurrentClassLogger();
|
||||||
|
var sw = Stopwatch.StartNew();
|
||||||
using (var uow = DbHandler.UnitOfWork())
|
using (var uow = DbHandler.UnitOfWork())
|
||||||
{
|
{
|
||||||
repeaters = new ConcurrentDictionary<ulong, RepeatRunner>(uow.Repeaters.GetAll().Select(r => new RepeatRunner(r)).Where(r => r != null).ToDictionary(r => r.Repeater.ChannelId));
|
repeaters = new ConcurrentDictionary<ulong, RepeatRunner>(uow.Repeaters.GetAll().Select(r => new RepeatRunner(r)).Where(r => r != null).ToDictionary(r => r.Repeater.ChannelId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sw.Stop();
|
||||||
|
_log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s");
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
|
@ -192,12 +192,9 @@ namespace NadekoBot.Modules.Administration
|
|||||||
guildConfig.GenerateCurrencyChannelIds = new HashSet<GCChannelId>(data.GenerateCurrencyChannels.Select(gc => new GCChannelId() { ChannelId = gc.Key }));
|
guildConfig.GenerateCurrencyChannelIds = new HashSet<GCChannelId>(data.GenerateCurrencyChannels.Select(gc => new GCChannelId() { ChannelId = gc.Key }));
|
||||||
selfAssRoles.AddRange(data.ListOfSelfAssignableRoles.Select(r => new SelfAssignedRole() { GuildId = guildConfig.GuildId, RoleId = r }).ToArray());
|
selfAssRoles.AddRange(data.ListOfSelfAssignableRoles.Select(r => new SelfAssignedRole() { GuildId = guildConfig.GuildId, RoleId = r }).ToArray());
|
||||||
var logSetting = guildConfig.LogSetting;
|
var logSetting = guildConfig.LogSetting;
|
||||||
guildConfig.LogSetting.IsLogging = data.LogChannel != null;
|
|
||||||
guildConfig.LogSetting.ChannelId = data.LogChannel ?? 0;
|
|
||||||
guildConfig.LogSetting.IgnoredChannels = new HashSet<IgnoredLogChannel>(data.LogserverIgnoreChannels.Select(id => new IgnoredLogChannel() { ChannelId = id }));
|
guildConfig.LogSetting.IgnoredChannels = new HashSet<IgnoredLogChannel>(data.LogserverIgnoreChannels.Select(id => new IgnoredLogChannel() { ChannelId = id }));
|
||||||
|
|
||||||
guildConfig.LogSetting.LogUserPresence = data.LogPresenceChannel != null;
|
guildConfig.LogSetting.LogUserPresenceId = data.LogPresenceChannel;
|
||||||
guildConfig.LogSetting.UserPresenceChannelId = data.LogPresenceChannel ?? 0;
|
|
||||||
|
|
||||||
|
|
||||||
guildConfig.FollowedStreams = new HashSet<FollowedStream>(data.ObservingStreams.Select(x =>
|
guildConfig.FollowedStreams = new HashSet<FollowedStream>(data.ObservingStreams.Select(x =>
|
||||||
|
@ -5,8 +5,11 @@ using NadekoBot.Attributes;
|
|||||||
using NadekoBot.Extensions;
|
using NadekoBot.Extensions;
|
||||||
using NadekoBot.Services;
|
using NadekoBot.Services;
|
||||||
using NadekoBot.Services.Database.Models;
|
using NadekoBot.Services.Database.Models;
|
||||||
|
using NLog;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
@ -21,8 +24,8 @@ namespace NadekoBot.Modules.Administration
|
|||||||
|
|
||||||
private static ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>> MutedUsers { get; } = new ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>>();
|
private static ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>> MutedUsers { get; } = new ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>>();
|
||||||
|
|
||||||
public static event Func<IGuildUser, MuteType, Task> UserMuted = delegate { return Task.CompletedTask; };
|
public static event Action<IGuildUser, MuteType> UserMuted = delegate { };
|
||||||
public static event Func<IGuildUser, MuteType, Task> UserUnmuted = delegate { return Task.CompletedTask; };
|
public static event Action<IGuildUser, MuteType> UserUnmuted = delegate { };
|
||||||
|
|
||||||
|
|
||||||
public enum MuteType {
|
public enum MuteType {
|
||||||
@ -31,32 +34,43 @@ namespace NadekoBot.Modules.Administration
|
|||||||
All
|
All
|
||||||
}
|
}
|
||||||
|
|
||||||
static MuteCommands() {
|
static MuteCommands()
|
||||||
using (var uow = DbHandler.UnitOfWork())
|
{
|
||||||
{
|
var _log = LogManager.GetCurrentClassLogger();
|
||||||
var configs = NadekoBot.AllGuildConfigs;
|
var sw = Stopwatch.StartNew();
|
||||||
GuildMuteRoles = new ConcurrentDictionary<ulong, string>(configs
|
|
||||||
.Where(c => !string.IsNullOrWhiteSpace(c.MuteRoleName))
|
|
||||||
.ToDictionary(c => c.GuildId, c => c.MuteRoleName));
|
|
||||||
|
|
||||||
MutedUsers = new ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>>(configs.ToDictionary(
|
var configs = NadekoBot.AllGuildConfigs;
|
||||||
k => k.GuildId,
|
GuildMuteRoles = new ConcurrentDictionary<ulong, string>(configs
|
||||||
v => new ConcurrentHashSet<ulong>(v.MutedUsers.Select(m => m.UserId))
|
.Where(c => !string.IsNullOrWhiteSpace(c.MuteRoleName))
|
||||||
));
|
.ToDictionary(c => c.GuildId, c => c.MuteRoleName));
|
||||||
}
|
|
||||||
|
MutedUsers = new ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>>(configs.ToDictionary(
|
||||||
|
k => k.GuildId,
|
||||||
|
v => new ConcurrentHashSet<ulong>(v.MutedUsers.Select(m => m.UserId))
|
||||||
|
));
|
||||||
|
|
||||||
NadekoBot.Client.UserJoined += Client_UserJoined;
|
NadekoBot.Client.UserJoined += Client_UserJoined;
|
||||||
|
|
||||||
|
sw.Stop();
|
||||||
|
_log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task Client_UserJoined(IGuildUser usr)
|
private static async void Client_UserJoined(IGuildUser usr)
|
||||||
{
|
{
|
||||||
ConcurrentHashSet<ulong> muted;
|
try
|
||||||
MutedUsers.TryGetValue(usr.Guild.Id, out muted);
|
{
|
||||||
|
ConcurrentHashSet<ulong> muted;
|
||||||
|
MutedUsers.TryGetValue(usr.Guild.Id, out muted);
|
||||||
|
|
||||||
if (muted == null || !muted.Contains(usr.Id))
|
if (muted == null || !muted.Contains(usr.Id))
|
||||||
return;
|
return;
|
||||||
else
|
else
|
||||||
await MuteCommands.MuteUser(usr).ConfigureAwait(false);
|
await Mute(usr).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_log.Warn(ex);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,7 +91,7 @@ namespace NadekoBot.Modules.Administration
|
|||||||
|
|
||||||
await uow.CompleteAsync().ConfigureAwait(false);
|
await uow.CompleteAsync().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
await UserMuted(usr, MuteType.All).ConfigureAwait(false);
|
UserMuted(usr, MuteType.All);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task UnmuteUser(IGuildUser usr)
|
public static async Task UnmuteUser(IGuildUser usr)
|
||||||
@ -96,7 +110,7 @@ namespace NadekoBot.Modules.Administration
|
|||||||
muted.TryRemove(usr.Id);
|
muted.TryRemove(usr.Id);
|
||||||
await uow.CompleteAsync().ConfigureAwait(false);
|
await uow.CompleteAsync().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
await UserUnmuted(usr, MuteType.All).ConfigureAwait(false);
|
UserUnmuted(usr, MuteType.All);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<IRole> GetMuteRole(IGuild guild)
|
public static async Task<IRole> GetMuteRole(IGuild guild)
|
||||||
@ -201,8 +215,8 @@ namespace NadekoBot.Modules.Administration
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await user.AddRolesAsync(await GetMuteRole(Context.Guild).ConfigureAwait(false)).ConfigureAwait(false);
|
await user.AddRolesAsync(await GetMuteRole(channel.Guild).ConfigureAwait(false)).ConfigureAwait(false);
|
||||||
await UserMuted(user, MuteType.Chat).ConfigureAwait(false);
|
UserMuted(user, MuteType.Chat);
|
||||||
await Context.Channel.SendConfirmAsync($"✏️🚫 **{user}** has been **muted** from chatting.").ConfigureAwait(false);
|
await Context.Channel.SendConfirmAsync($"✏️🚫 **{user}** has been **muted** from chatting.").ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
@ -218,8 +232,8 @@ namespace NadekoBot.Modules.Administration
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await user.RemoveRolesAsync(await GetMuteRole(Context.Guild).ConfigureAwait(false)).ConfigureAwait(false);
|
await user.RemoveRolesAsync(await GetMuteRole(channel.Guild).ConfigureAwait(false)).ConfigureAwait(false);
|
||||||
await UserUnmuted(user, MuteType.Chat).ConfigureAwait(false);
|
UserUnmuted(user, MuteType.Chat);
|
||||||
await Context.Channel.SendConfirmAsync($"✏️✅ **{user}** has been **unmuted** from chatting.").ConfigureAwait(false);
|
await Context.Channel.SendConfirmAsync($"✏️✅ **{user}** has been **unmuted** from chatting.").ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
@ -236,7 +250,7 @@ namespace NadekoBot.Modules.Administration
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
await user.ModifyAsync(usr => usr.Mute = true).ConfigureAwait(false);
|
await user.ModifyAsync(usr => usr.Mute = true).ConfigureAwait(false);
|
||||||
await UserMuted(user, MuteType.Voice).ConfigureAwait(false);
|
UserMuted(user, MuteType.Voice);
|
||||||
await Context.Channel.SendConfirmAsync($"🎙🚫 **{user}** has been **voice muted**.").ConfigureAwait(false);
|
await Context.Channel.SendConfirmAsync($"🎙🚫 **{user}** has been **voice muted**.").ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
@ -253,7 +267,7 @@ namespace NadekoBot.Modules.Administration
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
await user.ModifyAsync(usr => usr.Mute = false).ConfigureAwait(false);
|
await user.ModifyAsync(usr => usr.Mute = false).ConfigureAwait(false);
|
||||||
await UserUnmuted(user, MuteType.Voice).ConfigureAwait(false);
|
UserUnmuted(user, MuteType.Voice);
|
||||||
await Context.Channel.SendConfirmAsync($"🎙✅ **{user}** has been **voice unmuted**.").ConfigureAwait(false);
|
await Context.Channel.SendConfirmAsync($"🎙✅ **{user}** has been **voice unmuted**.").ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
|
@ -7,6 +7,7 @@ using NadekoBot.Services.Database.Models;
|
|||||||
using NLog;
|
using NLog;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
@ -21,8 +22,11 @@ namespace NadekoBot.Modules.Administration
|
|||||||
public static List<PlayingStatus> RotatingStatusMessages { get; }
|
public static List<PlayingStatus> RotatingStatusMessages { get; }
|
||||||
public static bool RotatingStatuses { get; private set; } = false;
|
public static bool RotatingStatuses { get; private set; } = false;
|
||||||
|
|
||||||
|
//todo wtf is with this while(true) in constructor
|
||||||
static PlayingRotateCommands()
|
static PlayingRotateCommands()
|
||||||
{
|
{
|
||||||
|
_log = LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
using (var uow = DbHandler.UnitOfWork())
|
using (var uow = DbHandler.UnitOfWork())
|
||||||
{
|
{
|
||||||
var conf = uow.BotConfig.GetOrCreate();
|
var conf = uow.BotConfig.GetOrCreate();
|
||||||
@ -30,7 +34,6 @@ namespace NadekoBot.Modules.Administration
|
|||||||
RotatingStatuses = conf.RotatingStatuses;
|
RotatingStatuses = conf.RotatingStatuses;
|
||||||
}
|
}
|
||||||
|
|
||||||
_log = LogManager.GetCurrentClassLogger();
|
|
||||||
var t = Task.Run(async () =>
|
var t = Task.Run(async () =>
|
||||||
{
|
{
|
||||||
var index = 0;
|
var index = 0;
|
||||||
|
@ -45,7 +45,8 @@ namespace NadekoBot.Modules.Administration
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
usr.MessageCount++;
|
usr.MessageCount++;
|
||||||
var t = Task.Run(async () => {
|
var t = Task.Run(async () =>
|
||||||
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await Task.Delay(PerSeconds * 1000, cancelSource.Token);
|
await Task.Delay(PerSeconds * 1000, cancelSource.Token);
|
||||||
@ -61,26 +62,28 @@ namespace NadekoBot.Modules.Administration
|
|||||||
|
|
||||||
static RatelimitCommand()
|
static RatelimitCommand()
|
||||||
{
|
{
|
||||||
_log = LogManager.GetCurrentClassLogger();
|
_log = LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
NadekoBot.Client.MessageReceived += (umsg) =>
|
NadekoBot.Client.MessageReceived += async (umsg) =>
|
||||||
{
|
{
|
||||||
var t = Task.Run(async () =>
|
try
|
||||||
{
|
{
|
||||||
var usrMsg = umsg as IUserMessage;
|
var usrMsg = umsg as IUserMessage;
|
||||||
var channel = umsg.Channel as ITextChannel;
|
if (usrMsg == null)
|
||||||
|
return;
|
||||||
|
var channel = usrMsg.Channel as ITextChannel;
|
||||||
|
|
||||||
if (channel == null || usrMsg.IsAuthor())
|
if (channel == null || usrMsg.IsAuthor())
|
||||||
return;
|
return;
|
||||||
Ratelimiter limiter;
|
Ratelimiter limiter;
|
||||||
if (!RatelimitingChannels.TryGetValue(channel.Id, out limiter))
|
if (!RatelimitingChannels.TryGetValue(channel.Id, out limiter))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (limiter.CheckUserRatelimit(umsg.Author.Id))
|
if (limiter.CheckUserRatelimit(usrMsg.Author.Id))
|
||||||
try { await usrMsg.DeleteAsync(); } catch (Exception ex) { _log.Warn(ex); }
|
await usrMsg.DeleteAsync();
|
||||||
});
|
}
|
||||||
return Task.CompletedTask;
|
catch (Exception ex) { _log.Warn(ex); }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
@ -115,6 +118,7 @@ namespace NadekoBot.Modules.Administration
|
|||||||
MaxMessages = msg,
|
MaxMessages = msg,
|
||||||
PerSeconds = perSec,
|
PerSeconds = perSec,
|
||||||
};
|
};
|
||||||
|
if (RatelimitingChannels.TryAdd(channel.Id, toAdd))
|
||||||
if(RatelimitingChannels.TryAdd(Context.Channel.Id, toAdd))
|
if(RatelimitingChannels.TryAdd(Context.Channel.Id, toAdd))
|
||||||
{
|
{
|
||||||
await Context.Channel.SendConfirmAsync("Slow mode initiated",
|
await Context.Channel.SendConfirmAsync("Slow mode initiated",
|
||||||
|
@ -192,12 +192,8 @@ namespace NadekoBot.Modules.Administration
|
|||||||
|
|
||||||
if (conf.AutoDeleteSelfAssignedRoleMessages)
|
if (conf.AutoDeleteSelfAssignedRoleMessages)
|
||||||
{
|
{
|
||||||
var t = Task.Run(async () =>
|
msg.DeleteAfter(3);
|
||||||
{
|
Context.Message.DeleteAfter(3);
|
||||||
await Task.Delay(3000).ConfigureAwait(false);
|
|
||||||
try { await msg.DeleteAsync().ConfigureAwait(false); } catch { } // if 502 or something, i don't want bot crashing
|
|
||||||
try { await Context.Message.DeleteAsync().ConfigureAwait(false); } catch { }
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -238,12 +234,8 @@ namespace NadekoBot.Modules.Administration
|
|||||||
|
|
||||||
if (autoDeleteSelfAssignedRoleMessages)
|
if (autoDeleteSelfAssignedRoleMessages)
|
||||||
{
|
{
|
||||||
var t = Task.Run(async () =>
|
msg.DeleteAfter(3);
|
||||||
{
|
Context.Message.DeleteAfter(3);
|
||||||
await Task.Delay(3000).ConfigureAwait(false);
|
|
||||||
try { await msg.DeleteAsync().ConfigureAwait(false); } catch { } // if 502 or something, i don't want bot crashing
|
|
||||||
try { await Context.Message.DeleteAsync().ConfigureAwait(false); } catch { }
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,101 +25,87 @@ namespace NadekoBot.Modules.Administration
|
|||||||
NadekoBot.Client.UserLeft += UserLeft;
|
NadekoBot.Client.UserLeft += UserLeft;
|
||||||
_log = LogManager.GetCurrentClassLogger();
|
_log = LogManager.GetCurrentClassLogger();
|
||||||
}
|
}
|
||||||
|
//todo optimize ASAP
|
||||||
private static Task UserLeft(SocketGuildUser user)
|
private static async void UserLeft(IGuildUser user)
|
||||||
{
|
{
|
||||||
var leftTask = Task.Run(async () =>
|
try
|
||||||
{
|
{
|
||||||
try
|
GuildConfig conf;
|
||||||
|
using (var uow = DbHandler.UnitOfWork())
|
||||||
{
|
{
|
||||||
GuildConfig conf;
|
conf = uow.GuildConfigs.For(user.Guild.Id, set => set);
|
||||||
using (var uow = DbHandler.UnitOfWork())
|
}
|
||||||
{
|
|
||||||
conf = uow.GuildConfigs.For(user.Guild.Id, set => set);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (!conf.SendChannelByeMessage) return;
|
||||||
|
var channel = (await user.Guild.GetTextChannelsAsync()).SingleOrDefault(c => c.Id == conf.ByeMessageChannelId);
|
||||||
if (!conf.SendChannelByeMessage) return;
|
if (!conf.SendChannelByeMessage) return;
|
||||||
var channel = (await user.Guild.GetTextChannelsAsync()).FirstOrDefault(c => c.Id == conf.ByeMessageChannelId);
|
var channel = (await user.Guild.GetTextChannelsAsync()).FirstOrDefault(c => c.Id == conf.ByeMessageChannelId);
|
||||||
|
|
||||||
if (channel == null) //maybe warn the server owner that the channel is missing
|
if (channel == null) //maybe warn the server owner that the channel is missing
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var msg = conf.ChannelByeMessageText.Replace("%user%", user.Username).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
|
var msg = conf.ChannelByeMessageText.Replace("%user%", user.Username).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
|
||||||
if (string.IsNullOrWhiteSpace(msg))
|
if (string.IsNullOrWhiteSpace(msg))
|
||||||
return;
|
return;
|
||||||
try
|
|
||||||
{
|
|
||||||
var toDelete = await channel.SendMessageAsync(msg.SanitizeMentions()).ConfigureAwait(false);
|
|
||||||
if (conf.AutoDeleteByeMessagesTimer > 0)
|
|
||||||
{
|
|
||||||
var t = Task.Run(async () =>
|
|
||||||
{
|
|
||||||
await Task.Delay(conf.AutoDeleteByeMessagesTimer * 1000).ConfigureAwait(false); // 5 minutes
|
|
||||||
try { await toDelete.DeleteAsync().ConfigureAwait(false); } catch { }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex) { _log.Warn(ex); }
|
|
||||||
}
|
|
||||||
catch { }
|
|
||||||
});
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Task UserJoined(IGuildUser user)
|
|
||||||
{
|
|
||||||
var joinedTask = Task.Run(async () =>
|
|
||||||
{
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
GuildConfig conf;
|
var toDelete = await channel.SendMessageAsync(msg.SanitizeMentions()).ConfigureAwait(false);
|
||||||
using (var uow = DbHandler.UnitOfWork())
|
if (conf.AutoDeleteByeMessagesTimer > 0)
|
||||||
{
|
{
|
||||||
conf = uow.GuildConfigs.For(user.Guild.Id, set => set);
|
toDelete.DeleteAfter(conf.AutoDeleteByeMessagesTimer);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex) { _log.Warn(ex); }
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
|
||||||
if (conf.SendChannelGreetMessage)
|
private static async void UserJoined(IGuildUser user)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
GuildConfig conf;
|
||||||
|
using (var uow = DbHandler.UnitOfWork())
|
||||||
|
{
|
||||||
|
conf = uow.GuildConfigs.For(user.Guild.Id, set => set);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conf.SendChannelGreetMessage)
|
||||||
|
{
|
||||||
|
var channel = (await user.Guild.GetTextChannelsAsync()).SingleOrDefault(c => c.Id == conf.GreetMessageChannelId);
|
||||||
|
if (channel != null) //maybe warn the server owner that the channel is missing
|
||||||
{
|
{
|
||||||
var channel = (await user.Guild.GetTextChannelsAsync()).FirstOrDefault(c => c.Id == conf.GreetMessageChannelId);
|
var msg = conf.ChannelGreetMessageText.Replace("%user%", user.Mention).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
|
||||||
if (channel != null) //maybe warn the server owner that the channel is missing
|
if (!string.IsNullOrWhiteSpace(msg))
|
||||||
{
|
{
|
||||||
var msg = conf.ChannelGreetMessageText.Replace("%user%", user.Mention).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
|
try
|
||||||
if (!string.IsNullOrWhiteSpace(msg))
|
|
||||||
{
|
{
|
||||||
try
|
var toDelete = await channel.SendMessageAsync(msg.SanitizeMentions()).ConfigureAwait(false);
|
||||||
|
if (conf.AutoDeleteGreetMessagesTimer > 0)
|
||||||
{
|
{
|
||||||
var toDelete = await channel.SendMessageAsync(msg.SanitizeMentions()).ConfigureAwait(false);
|
toDelete.DeleteAfter(conf.AutoDeleteGreetMessagesTimer);
|
||||||
if (conf.AutoDeleteGreetMessagesTimer > 0)
|
|
||||||
{
|
|
||||||
var t = Task.Run(async () =>
|
|
||||||
{
|
|
||||||
await Task.Delay(conf.AutoDeleteGreetMessagesTimer * 1000).ConfigureAwait(false); // 5 minutes
|
|
||||||
try { await toDelete.DeleteAsync().ConfigureAwait(false); } catch { }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (Exception ex) { _log.Warn(ex); }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (conf.SendDmGreetMessage)
|
|
||||||
{
|
|
||||||
var channel = await user.CreateDMChannelAsync();
|
|
||||||
|
|
||||||
if (channel != null)
|
|
||||||
{
|
|
||||||
var msg = conf.DmGreetMessageText.Replace("%user%", user.Username).Replace("%server%", user.Guild.Name);
|
|
||||||
if (!string.IsNullOrWhiteSpace(msg))
|
|
||||||
{
|
|
||||||
await channel.SendConfirmAsync(msg).ConfigureAwait(false);
|
|
||||||
}
|
}
|
||||||
|
catch (Exception ex) { _log.Warn(ex); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch { }
|
|
||||||
});
|
if (conf.SendDmGreetMessage)
|
||||||
return Task.CompletedTask;
|
{
|
||||||
|
var channel = await user.CreateDMChannelAsync();
|
||||||
|
|
||||||
|
if (channel != null)
|
||||||
|
{
|
||||||
|
var msg = conf.DmGreetMessageText.Replace("%user%", user.Username).Replace("%server%", user.Guild.Name);
|
||||||
|
if (!string.IsNullOrWhiteSpace(msg))
|
||||||
|
{
|
||||||
|
await channel.SendConfirmAsync(msg).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
|
@ -4,8 +4,10 @@ using Discord.WebSocket;
|
|||||||
using NadekoBot.Attributes;
|
using NadekoBot.Attributes;
|
||||||
using NadekoBot.Extensions;
|
using NadekoBot.Extensions;
|
||||||
using NadekoBot.Services;
|
using NadekoBot.Services;
|
||||||
|
using NLog;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@ -22,48 +24,52 @@ namespace NadekoBot.Modules.Administration
|
|||||||
private static ConcurrentHashSet<ulong> voicePlusTextCache { get; }
|
private static ConcurrentHashSet<ulong> voicePlusTextCache { get; }
|
||||||
static VoicePlusTextCommands()
|
static VoicePlusTextCommands()
|
||||||
{
|
{
|
||||||
|
var _log = LogManager.GetCurrentClassLogger();
|
||||||
|
var sw = Stopwatch.StartNew();
|
||||||
using (var uow = DbHandler.UnitOfWork())
|
using (var uow = DbHandler.UnitOfWork())
|
||||||
{
|
{
|
||||||
voicePlusTextCache = new ConcurrentHashSet<ulong>(NadekoBot.AllGuildConfigs.Where(g => g.VoicePlusTextEnabled).Select(g => g.GuildId));
|
voicePlusTextCache = new ConcurrentHashSet<ulong>(NadekoBot.AllGuildConfigs.Where(g => g.VoicePlusTextEnabled).Select(g => g.GuildId));
|
||||||
}
|
}
|
||||||
NadekoBot.Client.UserVoiceStateUpdated += UserUpdatedEventHandler;
|
NadekoBot.Client.UserVoiceStateUpdated += UserUpdatedEventHandler;
|
||||||
|
|
||||||
|
sw.Stop();
|
||||||
|
_log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Task UserUpdatedEventHandler(SocketUser iuser, SocketVoiceState before, SocketVoiceState after)
|
private static async void UserUpdatedEventHandler(SocketUser iuser, IVoiceState before, IVoiceState after)
|
||||||
{
|
{
|
||||||
var user = (iuser as SocketGuildUser);
|
var user = (iuser as SocketGuildUser);
|
||||||
var guild = user?.Guild;
|
var guild = user?.Guild;
|
||||||
|
|
||||||
if (guild == null)
|
if (guild == null)
|
||||||
return Task.CompletedTask;
|
return;
|
||||||
var task = Task.Run(async () =>
|
|
||||||
|
try
|
||||||
{
|
{
|
||||||
try
|
var botUserPerms = guild.GetCurrentUser().GuildPermissions;
|
||||||
|
|
||||||
|
if (before.VoiceChannel == after.VoiceChannel) return;
|
||||||
|
|
||||||
|
if (!voicePlusTextCache.Contains(guild.Id))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!botUserPerms.ManageChannels || !botUserPerms.ManageRoles)
|
||||||
{
|
{
|
||||||
var botUserPerms = guild.CurrentUser.GuildPermissions;
|
try
|
||||||
|
|
||||||
if (before.VoiceChannel == after.VoiceChannel) return;
|
|
||||||
|
|
||||||
if (!voicePlusTextCache.Contains(guild.Id))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!botUserPerms.ManageChannels || !botUserPerms.ManageRoles)
|
|
||||||
{
|
{
|
||||||
try
|
await (await guild.GetOwnerAsync()).SendErrorAsync(
|
||||||
{
|
"⚠️ I don't have **manage server** and/or **manage channels** permission," +
|
||||||
await (await guild.GetOwnerAsync()).SendErrorAsync(
|
$" so I cannot run `voice+text` on **{guild.Name}** server.").ConfigureAwait(false);
|
||||||
"⚠️ I don't have **manage server** and/or **manage channels** permission," +
|
|
||||||
$" so I cannot run `voice+text` on **{guild.Name}** server.").ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
catch { }
|
|
||||||
using (var uow = DbHandler.UnitOfWork())
|
|
||||||
{
|
|
||||||
uow.GuildConfigs.For(guild.Id, set => set).VoicePlusTextEnabled = false;
|
|
||||||
voicePlusTextCache.TryRemove(guild.Id);
|
|
||||||
await uow.CompleteAsync().ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
catch { }
|
||||||
|
using (var uow = DbHandler.UnitOfWork())
|
||||||
|
{
|
||||||
|
uow.GuildConfigs.For(guild.Id, set => set).VoicePlusTextEnabled = false;
|
||||||
|
voicePlusTextCache.TryRemove(guild.Id);
|
||||||
|
await uow.CompleteAsync().ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
var beforeVch = before.VoiceChannel;
|
var beforeVch = before.VoiceChannel;
|
||||||
@ -98,7 +104,6 @@ namespace NadekoBot.Modules.Administration
|
|||||||
Console.WriteLine(ex);
|
Console.WriteLine(ex);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetChannelName(string voiceName) =>
|
private static string GetChannelName(string voiceName) =>
|
||||||
|
@ -11,6 +11,8 @@ using NadekoBot.Services.Database.Models;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NadekoBot.Extensions;
|
using NadekoBot.Extensions;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using NLog;
|
||||||
|
|
||||||
namespace NadekoBot.Modules.ClashOfClans
|
namespace NadekoBot.Modules.ClashOfClans
|
||||||
{
|
{
|
||||||
@ -21,8 +23,12 @@ namespace NadekoBot.Modules.ClashOfClans
|
|||||||
|
|
||||||
private static Timer checkWarTimer { get; }
|
private static Timer checkWarTimer { get; }
|
||||||
|
|
||||||
|
private static new readonly Logger _log;
|
||||||
|
|
||||||
static ClashOfClans()
|
static ClashOfClans()
|
||||||
{
|
{
|
||||||
|
_log = LogManager.GetCurrentClassLogger();
|
||||||
|
var sw = Stopwatch.StartNew();
|
||||||
using (var uow = DbHandler.UnitOfWork())
|
using (var uow = DbHandler.UnitOfWork())
|
||||||
{
|
{
|
||||||
ClashWars = new ConcurrentDictionary<ulong, List<ClashWar>>(
|
ClashWars = new ConcurrentDictionary<ulong, List<ClashWar>>(
|
||||||
@ -36,7 +42,7 @@ namespace NadekoBot.Modules.ClashOfClans
|
|||||||
.GetResult();
|
.GetResult();
|
||||||
return cw;
|
return cw;
|
||||||
})
|
})
|
||||||
.Where(cw => cw?.Channel != null)
|
.Where(cw => cw.Channel != null)
|
||||||
.GroupBy(cw => cw.GuildId)
|
.GroupBy(cw => cw.GuildId)
|
||||||
.ToDictionary(g => g.Key, g => g.ToList()));
|
.ToDictionary(g => g.Key, g => g.ToList()));
|
||||||
}
|
}
|
||||||
@ -55,6 +61,20 @@ namespace NadekoBot.Modules.ClashOfClans
|
|||||||
public ClashOfClans() : base()
|
public ClashOfClans() : base()
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
|
checkWarTimer = new Timer(async _ =>
|
||||||
|
{
|
||||||
|
foreach (var kvp in ClashWars)
|
||||||
|
{
|
||||||
|
foreach (var war in kvp.Value)
|
||||||
|
{
|
||||||
|
try { await CheckWar(TimeSpan.FromHours(2), war).ConfigureAwait(false); } catch { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, null, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1));
|
||||||
|
|
||||||
|
sw.Stop();
|
||||||
|
_log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task CheckWar(TimeSpan callExpire, ClashWar war)
|
private static async Task CheckWar(TimeSpan callExpire, ClashWar war)
|
||||||
|
@ -7,7 +7,8 @@ using System.Collections.Concurrent;
|
|||||||
using NadekoBot.Services.Database.Models;
|
using NadekoBot.Services.Database.Models;
|
||||||
using Discord;
|
using Discord;
|
||||||
using NadekoBot.Extensions;
|
using NadekoBot.Extensions;
|
||||||
using Discord.WebSocket;
|
using NLog;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
namespace NadekoBot.Modules.CustomReactions
|
namespace NadekoBot.Modules.CustomReactions
|
||||||
{
|
{
|
||||||
@ -19,14 +20,20 @@ namespace NadekoBot.Modules.CustomReactions
|
|||||||
|
|
||||||
public static ConcurrentDictionary<string, uint> ReactionStats { get; } = new ConcurrentDictionary<string, uint>();
|
public static ConcurrentDictionary<string, uint> ReactionStats { get; } = new ConcurrentDictionary<string, uint>();
|
||||||
|
|
||||||
|
private static new readonly Logger _log;
|
||||||
|
|
||||||
static CustomReactions()
|
static CustomReactions()
|
||||||
{
|
{
|
||||||
|
_log = LogManager.GetCurrentClassLogger();
|
||||||
|
var sw = Stopwatch.StartNew();
|
||||||
using (var uow = DbHandler.UnitOfWork())
|
using (var uow = DbHandler.UnitOfWork())
|
||||||
{
|
{
|
||||||
var items = uow.CustomReactions.GetAll();
|
var items = uow.CustomReactions.GetAll();
|
||||||
GuildReactions = new ConcurrentDictionary<ulong, ConcurrentHashSet<CustomReaction>>(items.Where(g => g.GuildId != null && g.GuildId != 0).GroupBy(k => k.GuildId.Value).ToDictionary(g => g.Key, g => new ConcurrentHashSet<CustomReaction>(g)));
|
GuildReactions = new ConcurrentDictionary<ulong, ConcurrentHashSet<CustomReaction>>(items.Where(g => g.GuildId != null && g.GuildId != 0).GroupBy(k => k.GuildId.Value).ToDictionary(g => g.Key, g => new ConcurrentHashSet<CustomReaction>(g)));
|
||||||
GlobalReactions = new ConcurrentHashSet<CustomReaction>(items.Where(g => g.GuildId == null || g.GuildId == 0));
|
GlobalReactions = new ConcurrentHashSet<CustomReaction>(items.Where(g => g.GuildId == null || g.GuildId == 0));
|
||||||
}
|
}
|
||||||
|
sw.Stop();
|
||||||
|
_log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s");
|
||||||
}
|
}
|
||||||
public CustomReactions() : base()
|
public CustomReactions() : base()
|
||||||
{
|
{
|
||||||
@ -117,7 +124,7 @@ namespace NadekoBot.Modules.CustomReactions
|
|||||||
reactions.Add(cr);
|
reactions.Add(cr);
|
||||||
}
|
}
|
||||||
|
|
||||||
await Context.Channel.EmbedAsync(new EmbedBuilder().WithColor(NadekoBot.OkColor)
|
await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||||
.WithTitle("New Custom Reaction")
|
.WithTitle("New Custom Reaction")
|
||||||
.WithDescription($"#{cr.Id}")
|
.WithDescription($"#{cr.Id}")
|
||||||
.AddField(efb => efb.WithName("Trigger").WithValue(key))
|
.AddField(efb => efb.WithName("Trigger").WithValue(key))
|
||||||
@ -170,7 +177,7 @@ namespace NadekoBot.Modules.CustomReactions
|
|||||||
{
|
{
|
||||||
var txtStream = await customReactions.GroupBy(cr => cr.Trigger)
|
var txtStream = await customReactions.GroupBy(cr => cr.Trigger)
|
||||||
.OrderBy(cr => cr.Key)
|
.OrderBy(cr => cr.Key)
|
||||||
.Select(cr => new { Trigger = cr.Key, Responses = cr.Select(y => y.Response).ToList() })
|
.Select(cr => new { Trigger = cr.Key, Responses = cr.Select(y => new { id = y.Id, text = y.Response }).ToList() })
|
||||||
.ToJson()
|
.ToJson()
|
||||||
.ToStream()
|
.ToStream()
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
@ -220,7 +227,7 @@ namespace NadekoBot.Modules.CustomReactions
|
|||||||
await Context.Channel.SendErrorAsync("No custom reaction found with that id.").ConfigureAwait(false);
|
await Context.Channel.SendErrorAsync("No custom reaction found with that id.").ConfigureAwait(false);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await Context.Channel.EmbedAsync(new EmbedBuilder().WithColor(NadekoBot.OkColor)
|
await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||||
.WithDescription($"#{id}")
|
.WithDescription($"#{id}")
|
||||||
.AddField(efb => efb.WithName("Trigger").WithValue(found.Trigger))
|
.AddField(efb => efb.WithName("Trigger").WithValue(found.Trigger))
|
||||||
.AddField(efb => efb.WithName("Response").WithValue(found.Response + "\n```css\n" + found.Response + "```"))
|
.AddField(efb => efb.WithName("Response").WithValue(found.Response + "\n```css\n" + found.Response + "```"))
|
||||||
@ -295,9 +302,9 @@ namespace NadekoBot.Modules.CustomReactions
|
|||||||
if (page < 1)
|
if (page < 1)
|
||||||
return;
|
return;
|
||||||
await Context.Channel.EmbedAsync(ReactionStats.OrderByDescending(x => x.Value)
|
await Context.Channel.EmbedAsync(ReactionStats.OrderByDescending(x => x.Value)
|
||||||
.Skip((page - 1)*9)
|
.Skip((page - 1) * 9)
|
||||||
.Take(9)
|
.Take(9)
|
||||||
.Aggregate(new EmbedBuilder().WithColor(NadekoBot.OkColor).WithTitle($"Custom Reaction stats page #{page}"),
|
.Aggregate(new EmbedBuilder().WithOkColor().WithTitle($"Custom Reaction stats page #{page}"),
|
||||||
(agg, cur) => agg.AddField(efb => efb.WithName(cur.Key).WithValue(cur.Value.ToString()).WithIsInline(true)))
|
(agg, cur) => agg.AddField(efb => efb.WithName(cur.Key).WithValue(cur.Value.ToString()).WithIsInline(true)))
|
||||||
)
|
)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
@ -28,13 +28,14 @@ namespace NadekoBot.Modules.Gambling
|
|||||||
var ar = new AnimalRace(Context.Guild.Id, (ITextChannel)Context.Channel);
|
var ar = new AnimalRace(Context.Guild.Id, (ITextChannel)Context.Channel);
|
||||||
|
|
||||||
if (ar.Fail)
|
if (ar.Fail)
|
||||||
await Context.Channel.SendErrorAsync("🏁 `Failed starting a race. Another race is probably running.`");
|
await Context.Channel.SendErrorAsync("🏁 `Failed starting a race. Another race is probably running.`").ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
public async Task JoinRace(int amount = 0)
|
public async Task JoinRace(int amount = 0)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (amount < 0)
|
if (amount < 0)
|
||||||
amount = 0;
|
amount = 0;
|
||||||
|
|
||||||
@ -42,7 +43,7 @@ namespace NadekoBot.Modules.Gambling
|
|||||||
AnimalRace ar;
|
AnimalRace ar;
|
||||||
if (!AnimalRaces.TryGetValue(Context.Guild.Id, out ar))
|
if (!AnimalRaces.TryGetValue(Context.Guild.Id, out ar))
|
||||||
{
|
{
|
||||||
await Context.Channel.SendErrorAsync("No race exists on this server");
|
await Context.Channel.SendErrorAsync("No race exists on this server").ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await ar.JoinRace(Context.User as IGuildUser, amount);
|
await ar.JoinRace(Context.User as IGuildUser, amount);
|
||||||
@ -87,21 +88,29 @@ namespace NadekoBot.Modules.Gambling
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
try { await raceChannel.SendConfirmAsync($"🏁`Race is starting in 20 seconds or when the room is full. Type {NadekoBot.ModulePrefixes[typeof(Gambling).Name]}jr to join the race.`"); } catch (Exception ex) { _log.Warn(ex); }
|
try
|
||||||
|
{
|
||||||
|
await raceChannel.SendConfirmAsync("Animal Race", $"Starting in 20 seconds or when the room is full.",
|
||||||
|
footer: $"Type {NadekoBot.ModulePrefixes[typeof(Gambling).Name]}jr to join the race.");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_log.Warn(ex);
|
||||||
|
}
|
||||||
var t = await Task.WhenAny(Task.Delay(20000, token), fullgame);
|
var t = await Task.WhenAny(Task.Delay(20000, token), fullgame);
|
||||||
Started = true;
|
Started = true;
|
||||||
cancelSource.Cancel();
|
cancelSource.Cancel();
|
||||||
if (t == fullgame)
|
if (t == fullgame)
|
||||||
{
|
{
|
||||||
try { await raceChannel.SendConfirmAsync("🏁`Race full, starting right now!`"); } catch (Exception ex) { _log.Warn(ex); }
|
try { await raceChannel.SendConfirmAsync("Animal Race", "Full! Starting immediately."); } catch (Exception ex) { _log.Warn(ex); }
|
||||||
}
|
}
|
||||||
else if (participants.Count > 1)
|
else if (participants.Count > 1)
|
||||||
{
|
{
|
||||||
try { await raceChannel.SendConfirmAsync("🏁`Game starting with " + participants.Count + " participants.`"); } catch (Exception ex) { _log.Warn(ex); }
|
try { await raceChannel.SendConfirmAsync("Animal Race", "Starting with " + participants.Count + " participants."); } catch (Exception ex) { _log.Warn(ex); }
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
try { await raceChannel.SendErrorAsync("🏁`Race failed to start since there was not enough participants.`"); } catch (Exception ex) { _log.Warn(ex); }
|
try { await raceChannel.SendErrorAsync("Animal Race", "Failed to start since there was not enough participants."); } catch (Exception ex) { _log.Warn(ex); }
|
||||||
var p = participants.FirstOrDefault();
|
var p = participants.FirstOrDefault();
|
||||||
|
|
||||||
if (p != null && p.AmountBet > 0)
|
if (p != null && p.AmountBet > 0)
|
||||||
@ -138,19 +147,26 @@ namespace NadekoBot.Modules.Gambling
|
|||||||
participants.ForEach(p =>
|
participants.ForEach(p =>
|
||||||
{
|
{
|
||||||
p.Total += 1 + rng.Next(0, 10);
|
p.Total += 1 + rng.Next(0, 10);
|
||||||
if (p.Total > 60)
|
|
||||||
{
|
|
||||||
p.Total = 60;
|
|
||||||
if (winner == null)
|
|
||||||
{
|
|
||||||
winner = p;
|
|
||||||
}
|
|
||||||
if (p.Place == 0)
|
|
||||||
p.Place = place++;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
participants
|
||||||
|
.OrderByDescending(p => p.Total)
|
||||||
|
.ForEach(p =>
|
||||||
|
{
|
||||||
|
if (p.Total > 60)
|
||||||
|
{
|
||||||
|
if (winner == null)
|
||||||
|
{
|
||||||
|
winner = p;
|
||||||
|
}
|
||||||
|
p.Total = 60;
|
||||||
|
if (p.Place == 0)
|
||||||
|
p.Place = place++;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
//draw the state
|
//draw the state
|
||||||
|
|
||||||
var text = $@"|🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🔚|
|
var text = $@"|🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🔚|
|
||||||
@ -181,25 +197,25 @@ namespace NadekoBot.Modules.Gambling
|
|||||||
{
|
{
|
||||||
var wonAmount = winner.AmountBet * (participants.Count - 1);
|
var wonAmount = winner.AmountBet * (participants.Count - 1);
|
||||||
|
|
||||||
await CurrencyHandler.AddCurrencyAsync(winner.User, "Won a Race", wonAmount, false).ConfigureAwait(false);
|
await CurrencyHandler.AddCurrencyAsync(winner.User, "Won a Race", wonAmount, true).ConfigureAwait(false);
|
||||||
await raceChannel.SendConfirmAsync($"🏁 {winner.User.Mention} as {winner.Animal} **Won the race and {wonAmount}{CurrencySign}!**").ConfigureAwait(false);
|
await raceChannel.SendConfirmAsync("Animal Race", $"{winner.User.Mention} as {winner.Animal} **Won the race and {wonAmount}{CurrencySign}!**").ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await raceChannel.SendConfirmAsync($"🏁 {winner.User.Mention} as {winner.Animal} **Won the race!**");
|
await raceChannel.SendConfirmAsync("Animal Race", $"{winner.User.Mention} as {winner.Animal} **Won the race!**").ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task Client_MessageReceived(SocketMessage imsg)
|
private void Client_MessageReceived(IMessage imsg)
|
||||||
{
|
{
|
||||||
var msg = imsg as IUserMessage;
|
var msg = imsg as IUserMessage;
|
||||||
if (msg == null)
|
if (msg == null)
|
||||||
return Task.CompletedTask;
|
return;
|
||||||
if (msg.IsAuthor() || !(imsg.Channel is SocketTextChannel) || imsg.Channel != raceChannel)
|
if (msg.IsAuthor() || !(imsg.Channel is ITextChannel) || imsg.Channel != raceChannel)
|
||||||
return Task.CompletedTask;
|
return;
|
||||||
messagesSinceGameStarted++;
|
messagesSinceGameStarted++;
|
||||||
return Task.CompletedTask;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task CheckForFullGameAsync(CancellationToken cancelToken)
|
private async Task CheckForFullGameAsync(CancellationToken cancelToken)
|
||||||
@ -215,28 +231,29 @@ namespace NadekoBot.Modules.Gambling
|
|||||||
var animal = "";
|
var animal = "";
|
||||||
if (!animals.TryDequeue(out animal))
|
if (!animals.TryDequeue(out animal))
|
||||||
{
|
{
|
||||||
await raceChannel.SendErrorAsync($"{u.Mention} `There is no running race on this server.`");
|
await raceChannel.SendErrorAsync($"{u.Mention} `There is no running race on this server.`").ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var p = new Participant(u, animal, amount);
|
var p = new Participant(u, animal, amount);
|
||||||
if (participants.Contains(p))
|
if (participants.Contains(p))
|
||||||
{
|
{
|
||||||
await raceChannel.SendErrorAsync($"{u.Mention} `You already joined this race.`");
|
await raceChannel.SendErrorAsync($"{u.Mention} `You already joined this race.`").ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (Started)
|
if (Started)
|
||||||
{
|
{
|
||||||
await raceChannel.SendErrorAsync($"{u.Mention} `Race is already started`");
|
await raceChannel.SendErrorAsync($"{u.Mention} `Race is already started`").ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (amount > 0)
|
if (amount > 0)
|
||||||
if (!await CurrencyHandler.RemoveCurrencyAsync(u, "BetRace", amount, true).ConfigureAwait(false))
|
if (!await CurrencyHandler.RemoveCurrencyAsync((IGuildUser)u, "BetRace", amount, false).ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
try { await raceChannel.SendErrorAsync($"{u.Mention} You don't have enough {Gambling.CurrencyName}s.").ConfigureAwait(false); } catch { }
|
try { await raceChannel.SendErrorAsync($"{u.Mention} You don't have enough {Gambling.CurrencyName}s.").ConfigureAwait(false); } catch { }
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
participants.Add(p);
|
participants.Add(p);
|
||||||
await raceChannel.SendConfirmAsync($"{u.Mention} **joined the race as a {p.Animal}" + (amount > 0 ? $" and bet {amount} {CurrencySign}!**" : "**"));
|
await raceChannel.SendConfirmAsync("Animal Race", $"{u.Mention} **joined as a {p.Animal}" + (amount > 0 ? $" and bet {amount} {CurrencySign}!**" : "**"))
|
||||||
|
.ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -258,10 +275,7 @@ namespace NadekoBot.Modules.Gambling
|
|||||||
this.AmountBet = amount;
|
this.AmountBet = amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override int GetHashCode()
|
public override int GetHashCode() => User.GetHashCode();
|
||||||
{
|
|
||||||
return User.GetHashCode();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool Equals(object obj)
|
public override bool Equals(object obj)
|
||||||
{
|
{
|
||||||
|
@ -10,25 +10,26 @@ using System.IO;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Image = ImageSharp.Image;
|
|
||||||
|
|
||||||
namespace NadekoBot.Modules.Gambling
|
namespace NadekoBot.Modules.Gambling
|
||||||
{
|
{
|
||||||
public partial class Gambling
|
public partial class Gambling
|
||||||
{
|
{
|
||||||
[Group]
|
[Group]
|
||||||
public class DriceRollCommands : ModuleBase
|
public class DriceRollCommands
|
||||||
{
|
{
|
||||||
private Regex dndRegex { get; } = new Regex(@"^(?<n1>\d+)d(?<n2>\d+)(?:\+(?<add>\d+))?(?:\-(?<sub>\d+))?$", RegexOptions.Compiled);
|
private Regex dndRegex { get; } = new Regex(@"^(?<n1>\d+)d(?<n2>\d+)(?:\+(?<add>\d+))?(?:\-(?<sub>\d+))?$", RegexOptions.Compiled);
|
||||||
|
private Regex fudgeRegex { get; } = new Regex(@"^(?<n1>\d+)d(?:F|f)$", RegexOptions.Compiled);
|
||||||
|
|
||||||
public enum RoleOrderType {
|
private readonly char[] fateRolls = new[] { '-', ' ', '+' };
|
||||||
Ordered,
|
|
||||||
Unordered
|
|
||||||
}
|
|
||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
public async Task Roll()
|
[RequireContext(ContextType.Guild)]
|
||||||
|
public async Task Roll(IUserMessage umsg)
|
||||||
{
|
{
|
||||||
|
var channel = (ITextChannel)umsg.Channel;
|
||||||
|
if (channel == null)
|
||||||
|
return;
|
||||||
var rng = new NadekoRandom();
|
var rng = new NadekoRandom();
|
||||||
var gen = rng.Next(1, 101);
|
var gen = rng.Next(1, 101);
|
||||||
|
|
||||||
@ -46,29 +47,57 @@ namespace NadekoBot.Modules.Gambling
|
|||||||
catch { return new MemoryStream(); }
|
catch { return new MemoryStream(); }
|
||||||
});
|
});
|
||||||
|
|
||||||
await Context.Channel.SendFileAsync(imageStream, "dice.png", $"{Context.User.Mention} rolled " + Format.Code(gen.ToString())).ConfigureAwait(false);
|
await channel.SendFileAsync(imageStream, "dice.png", $"{umsg.Author.Mention} rolled " + Format.Code(gen.ToString())).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum RollOrderType
|
||||||
|
{
|
||||||
|
Ordered,
|
||||||
|
Unordered
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
[Priority(0)]
|
[Priority(0)]
|
||||||
public async Task Roll(int num)
|
public async Task Roll(IUserMessage umsg, int num)
|
||||||
{
|
{
|
||||||
await InternalRoll(num, RoleOrderType.Ordered).ConfigureAwait(false);
|
await InternalRoll(umsg, num, true).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
[Priority(0)]
|
||||||
|
public async Task Rolluo(IUserMessage umsg, int num)
|
||||||
|
{
|
||||||
|
await InternalRoll(umsg, num, false).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
public async Task Rolluo(int num)
|
[RequireContext(ContextType.Guild)]
|
||||||
|
[Priority(1)]
|
||||||
|
public async Task Roll(IUserMessage umsg, string arg)
|
||||||
{
|
{
|
||||||
await InternalRoll(num, RoleOrderType.Unordered).ConfigureAwait(false);
|
await InternallDndRoll(umsg, arg, true).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
private async Task InternalRoll(int num, RoleOrderType ordType)
|
[RequireContext(ContextType.Guild)]
|
||||||
|
[Priority(1)]
|
||||||
|
public async Task Rolluo(IUserMessage umsg, string arg)
|
||||||
{
|
{
|
||||||
var ordered = ordType == RoleOrderType.Ordered;
|
await InternallDndRoll(umsg, arg, false).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task InternalRoll(IUserMessage umsg, int num, bool ordered)
|
||||||
|
{
|
||||||
|
var channel = (ITextChannel)umsg.Channel;
|
||||||
|
if (channel == null)
|
||||||
|
return;
|
||||||
|
|
||||||
if (num < 1 || num > 30)
|
if (num < 1 || num > 30)
|
||||||
{
|
{
|
||||||
await Context.Channel.SendErrorAsync("Invalid number specified. You can roll up to 1-30 dice at a time.").ConfigureAwait(false);
|
await channel.SendErrorAsync("Invalid number specified. You can roll up to 1-30 dice at a time.").ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,31 +135,38 @@ namespace NadekoBot.Modules.Gambling
|
|||||||
var ms = new MemoryStream();
|
var ms = new MemoryStream();
|
||||||
bitmap.SaveAsPng(ms);
|
bitmap.SaveAsPng(ms);
|
||||||
ms.Position = 0;
|
ms.Position = 0;
|
||||||
await Context.Channel.SendFileAsync(ms, "dice.png", $"{Context.User.Mention} rolled {values.Count} {(values.Count == 1 ? "die" : "dice")}. Total: **{values.Sum()}** Average: **{(values.Sum() / (1.0f * values.Count)).ToString("N2")}**").ConfigureAwait(false);
|
await channel.SendFileAsync(ms, "dice.png", $"{umsg.Author.Mention} rolled {values.Count} {(values.Count == 1 ? "die" : "dice")}. Total: **{values.Sum()}** Average: **{(values.Sum() / (1.0f * values.Count)).ToString("N2")}**").ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
private async Task InternallDndRoll(IUserMessage umsg, string arg, bool ordered)
|
||||||
[Priority(1)]
|
|
||||||
public async Task Roll(string arg)
|
|
||||||
{
|
{
|
||||||
await InternalDndRoll(arg, RoleOrderType.Ordered).ConfigureAwait(false);
|
var channel = (ITextChannel)umsg.Channel;
|
||||||
}
|
if (channel == null)
|
||||||
|
return;
|
||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
|
||||||
public async Task Rolluo(string arg)
|
|
||||||
{
|
|
||||||
await InternalDndRoll(arg, RoleOrderType.Unordered).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task InternalDndRoll(string arg, RoleOrderType ordType)
|
|
||||||
{
|
|
||||||
var ordered = ordType == RoleOrderType.Ordered;
|
|
||||||
var rng = new NadekoRandom();
|
|
||||||
Match match;
|
Match match;
|
||||||
if ((match = dndRegex.Match(arg)).Length != 0)
|
int n1;
|
||||||
|
int n2;
|
||||||
|
if ((match = fudgeRegex.Match(arg)).Length != 0 &&
|
||||||
|
int.TryParse(match.Groups["n1"].ToString(), out n1) &&
|
||||||
|
n1 > 0 && n1 < 500)
|
||||||
{
|
{
|
||||||
int n1;
|
var rng = new NadekoRandom();
|
||||||
int n2;
|
|
||||||
|
var rolls = new List<char>();
|
||||||
|
|
||||||
|
for (int i = 0; i < n1; i++)
|
||||||
|
{
|
||||||
|
rolls.Add(fateRolls[rng.Next(0, fateRolls.Length)]);
|
||||||
|
}
|
||||||
|
var embed = new EmbedBuilder().WithOkColor().WithDescription($"{umsg.Author.Mention} rolled {n1} fate {(n1 == 1 ? "die" : "dice")}.")
|
||||||
|
.AddField(efb => efb.WithName(Format.Bold("Result"))
|
||||||
|
.WithValue(string.Join(" ", rolls.Select(c => Format.Code($"[{c}]")))));
|
||||||
|
await channel.EmbedAsync(embed.Build()).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
else if ((match = dndRegex.Match(arg)).Length != 0)
|
||||||
|
{
|
||||||
|
var rng = new NadekoRandom();
|
||||||
if (int.TryParse(match.Groups["n1"].ToString(), out n1) &&
|
if (int.TryParse(match.Groups["n1"].ToString(), out n1) &&
|
||||||
int.TryParse(match.Groups["n2"].ToString(), out n2) &&
|
int.TryParse(match.Groups["n2"].ToString(), out n2) &&
|
||||||
n1 <= 50 && n2 <= 100000 && n1 > 0 && n2 > 0)
|
n1 <= 50 && n2 <= 100000 && n1 > 0 && n2 > 0)
|
||||||
@ -145,15 +181,21 @@ namespace NadekoBot.Modules.Gambling
|
|||||||
{
|
{
|
||||||
arr[i] = rng.Next(1, n2 + 1) + add - sub;
|
arr[i] = rng.Next(1, n2 + 1) + add - sub;
|
||||||
}
|
}
|
||||||
var elemCnt = 0;
|
|
||||||
await Context.Channel.SendConfirmAsync($"{Context.User.Mention} rolled {n1} {(n1 == 1 ? "die" : "dice")} `1 to {n2}` +`{add}` -`{sub}`.\n`Result:` " + string.Join(", ", (ordered ? arr.OrderBy(x => x).AsEnumerable() : arr).Select(x => elemCnt++ % 2 == 0 ? $"**{x}**" : x.ToString()))).ConfigureAwait(false);
|
var embed = new EmbedBuilder().WithOkColor().WithDescription($"{umsg.Author.Mention} rolled {n1} {(n1 == 1 ? "die" : "dice")} `1 to {n2}` +`{add}` -`{sub}`")
|
||||||
|
.AddField(efb => efb.WithName(Format.Bold("Result"))
|
||||||
|
.WithValue(string.Join(" ", (ordered ? arr.OrderBy(x => x).AsEnumerable() : arr).Select(x => Format.Code(x.ToString())))));
|
||||||
|
await channel.EmbedAsync(embed.Build()).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
public async Task NRoll([Remainder] string range)
|
[RequireContext(ContextType.Guild)]
|
||||||
|
public async Task NRoll(IUserMessage umsg, [Remainder] string range)
|
||||||
{
|
{
|
||||||
|
var channel = (ITextChannel)umsg.Channel;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
int rolled;
|
int rolled;
|
||||||
@ -172,11 +214,11 @@ namespace NadekoBot.Modules.Gambling
|
|||||||
rolled = new NadekoRandom().Next(0, int.Parse(range) + 1);
|
rolled = new NadekoRandom().Next(0, int.Parse(range) + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
await Context.Channel.SendConfirmAsync($"{Context.User.Mention} rolled **{rolled}**.").ConfigureAwait(false);
|
await channel.SendConfirmAsync($"{umsg.Author.Mention} rolled **{rolled}**.").ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await Context.Channel.SendErrorAsync($":anger: {ex.Message}").ConfigureAwait(false);
|
await channel.SendErrorAsync($":anger: {ex.Message}").ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,8 +70,6 @@ namespace NadekoBot.Modules.Gambling
|
|||||||
await Context.Channel.SendErrorAsync($"{Context.User.Mention} You don't have enough {Gambling.CurrencyPluralName}.").ConfigureAwait(false);
|
await Context.Channel.SendErrorAsync($"{Context.User.Mention} You don't have enough {Gambling.CurrencyPluralName}.").ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await CurrencyHandler.RemoveCurrencyAsync(guildUser, "Betflip Gamble", amount, false).ConfigureAwait(false);
|
|
||||||
//heads = true
|
//heads = true
|
||||||
//tails = false
|
//tails = false
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ namespace NadekoBot.Modules.Gambling
|
|||||||
public static string CurrencyPluralName { get; set; }
|
public static string CurrencyPluralName { get; set; }
|
||||||
public static string CurrencySign { get; set; }
|
public static string CurrencySign { get; set; }
|
||||||
|
|
||||||
public Gambling() : base()
|
static Gambling()
|
||||||
{
|
{
|
||||||
using (var uow = DbHandler.UnitOfWork())
|
using (var uow = DbHandler.UnitOfWork())
|
||||||
{
|
{
|
||||||
|
291
src/NadekoBot/Modules/Games/Commands/Acropobia.cs
Normal file
291
src/NadekoBot/Modules/Games/Commands/Acropobia.cs
Normal file
@ -0,0 +1,291 @@
|
|||||||
|
using Discord;
|
||||||
|
using Discord.Commands;
|
||||||
|
using NadekoBot.Attributes;
|
||||||
|
using NadekoBot.Extensions;
|
||||||
|
using NadekoBot.Services;
|
||||||
|
using NLog;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Immutable;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace NadekoBot.Modules.Games
|
||||||
|
{
|
||||||
|
public partial class Games
|
||||||
|
{
|
||||||
|
[Group]
|
||||||
|
public class Acropobia
|
||||||
|
{
|
||||||
|
//channelId, game
|
||||||
|
public static ConcurrentDictionary<ulong, AcrophobiaGame> AcrophobiaGames { get; } = new ConcurrentDictionary<ulong, AcrophobiaGame>();
|
||||||
|
|
||||||
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
public async Task Acro(IUserMessage imsg, int time = 60)
|
||||||
|
{
|
||||||
|
var channel = (ITextChannel)imsg.Channel;
|
||||||
|
|
||||||
|
var game = new AcrophobiaGame(channel, time);
|
||||||
|
if (AcrophobiaGames.TryAdd(channel.Id, game))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await game.Run();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
game.EnsureStopped();
|
||||||
|
AcrophobiaGames.TryRemove(channel.Id, out game);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await channel.SendErrorAsync("Acrophobia game is already running in this channel.").ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum AcroPhase
|
||||||
|
{
|
||||||
|
Submitting,
|
||||||
|
Idle, // used to wait for some other actions while transitioning through phases
|
||||||
|
Voting
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AcrophobiaGame
|
||||||
|
{
|
||||||
|
private readonly ITextChannel channel;
|
||||||
|
private readonly int time;
|
||||||
|
private readonly NadekoRandom rng;
|
||||||
|
private readonly ImmutableArray<char> startingLetters;
|
||||||
|
private readonly CancellationTokenSource source;
|
||||||
|
private AcroPhase phase { get; set; } = AcroPhase.Submitting;
|
||||||
|
|
||||||
|
private readonly ConcurrentDictionary<string, IGuildUser> submissions = new ConcurrentDictionary<string, IGuildUser>();
|
||||||
|
public IReadOnlyDictionary<string, IGuildUser> Submissions => submissions;
|
||||||
|
|
||||||
|
private readonly ConcurrentHashSet<ulong> usersWhoVoted = new ConcurrentHashSet<ulong>();
|
||||||
|
|
||||||
|
private int spamCount = 0;
|
||||||
|
|
||||||
|
//text, votes
|
||||||
|
private readonly ConcurrentDictionary<string, int> votes = new ConcurrentDictionary<string, int>();
|
||||||
|
private readonly Logger _log;
|
||||||
|
|
||||||
|
public AcrophobiaGame(ITextChannel channel, int time)
|
||||||
|
{
|
||||||
|
this._log = LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
this.channel = channel;
|
||||||
|
this.time = time;
|
||||||
|
this.source = new CancellationTokenSource();
|
||||||
|
|
||||||
|
this.rng = new NadekoRandom();
|
||||||
|
var wordCount = rng.Next(3, 6);
|
||||||
|
|
||||||
|
var lettersArr = new char[wordCount];
|
||||||
|
|
||||||
|
for (int i = 0; i < wordCount; i++)
|
||||||
|
{
|
||||||
|
var randChar = (char)rng.Next(65, 91);
|
||||||
|
lettersArr[i] = randChar == 'X' ? (char)rng.Next(65, 88) : randChar;
|
||||||
|
}
|
||||||
|
startingLetters = lettersArr.ToImmutableArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private EmbedBuilder GetEmbed()
|
||||||
|
{
|
||||||
|
var i = 0;
|
||||||
|
return phase == AcroPhase.Submitting
|
||||||
|
|
||||||
|
? new EmbedBuilder().WithOkColor()
|
||||||
|
.WithTitle("Acrophobia")
|
||||||
|
.WithDescription($"Game started. Create a sentence with the following acronym: **{string.Join(".", startingLetters)}.**\n")
|
||||||
|
.WithFooter(efb => efb.WithText("You have " + this.time + " seconds to make a submission."))
|
||||||
|
|
||||||
|
: new EmbedBuilder()
|
||||||
|
.WithOkColor()
|
||||||
|
.WithTitle("Acrophobia - Submissions Closed")
|
||||||
|
.WithDescription($@"Acronym was **{string.Join(".", startingLetters)}.**
|
||||||
|
--
|
||||||
|
{this.submissions.Aggregate("", (agg, cur) => agg + $"`{++i}.` **{cur.Key.ToLowerInvariant().ToTitleCase()}**\n")}
|
||||||
|
--")
|
||||||
|
.WithFooter(efb => efb.WithText("Vote by typing a number of the submission"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Run()
|
||||||
|
{
|
||||||
|
NadekoBot.Client.MessageReceived += PotentialAcro;
|
||||||
|
var embed = GetEmbed();
|
||||||
|
|
||||||
|
//SUBMISSIONS PHASE
|
||||||
|
await channel.EmbedAsync(embed.Build()).ConfigureAwait(false);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await Task.Delay(time * 1000, source.Token).ConfigureAwait(false);
|
||||||
|
phase = AcroPhase.Idle;
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//var i = 0;
|
||||||
|
if (submissions.Count == 0)
|
||||||
|
{
|
||||||
|
await channel.SendErrorAsync("Acrophobia", "Game ended with no submissions.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (submissions.Count == 1)
|
||||||
|
{
|
||||||
|
await channel.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||||
|
.WithDescription($"{submissions.First().Value.Mention} is the winner for being the only user who made a submission!")
|
||||||
|
.WithFooter(efb => efb.WithText(submissions.First().Key.ToLowerInvariant().ToTitleCase()))
|
||||||
|
.Build()).ConfigureAwait(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var submissionClosedEmbed = GetEmbed();
|
||||||
|
|
||||||
|
await channel.EmbedAsync(submissionClosedEmbed.Build()).ConfigureAwait(false);
|
||||||
|
|
||||||
|
//VOTING PHASE
|
||||||
|
this.phase = AcroPhase.Voting;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
//30 secondds for voting
|
||||||
|
await Task.Delay(30000, source.Token).ConfigureAwait(false);
|
||||||
|
this.phase = AcroPhase.Idle;
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await End().ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void PotentialAcro(IMessage arg)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var msg = arg as IUserMessage;
|
||||||
|
if (msg == null || msg.Author.IsBot || msg.Channel.Id != channel.Id)
|
||||||
|
return;
|
||||||
|
|
||||||
|
++spamCount;
|
||||||
|
|
||||||
|
var guildUser = (IGuildUser)msg.Author;
|
||||||
|
|
||||||
|
var input = msg.Content.ToUpperInvariant().Trim();
|
||||||
|
|
||||||
|
if (phase == AcroPhase.Submitting)
|
||||||
|
{
|
||||||
|
if (spamCount > 10)
|
||||||
|
{
|
||||||
|
spamCount = 0;
|
||||||
|
try { await channel.EmbedAsync(GetEmbed().Build()).ConfigureAwait(false); }
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
//user didn't input something already
|
||||||
|
IGuildUser throwaway;
|
||||||
|
if (submissions.TryGetValue(input, out throwaway))
|
||||||
|
return;
|
||||||
|
var inputWords = input.Split(' '); //get all words
|
||||||
|
|
||||||
|
if (inputWords.Length != startingLetters.Length) // number of words must be the same as the number of the starting letters
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (int i = 0; i < startingLetters.Length; i++)
|
||||||
|
{
|
||||||
|
var letter = startingLetters[i];
|
||||||
|
|
||||||
|
if (!inputWords[i].StartsWith(letter.ToString())) // all first letters must match
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//try adding it to the list of answers
|
||||||
|
if (!submissions.TryAdd(input, guildUser))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// all good. valid input. answer recorded
|
||||||
|
await channel.SendConfirmAsync("Acrophobia", $"{guildUser.Mention} submitted their sentence. ({submissions.Count} total)");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await msg.DeleteAsync();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
await msg.DeleteAsync(); //try twice
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (phase == AcroPhase.Voting)
|
||||||
|
{
|
||||||
|
if (spamCount > 10)
|
||||||
|
{
|
||||||
|
spamCount = 0;
|
||||||
|
try { await channel.EmbedAsync(GetEmbed().Build()).ConfigureAwait(false); }
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
|
||||||
|
IGuildUser usr;
|
||||||
|
//if (submissions.TryGetValue(input, out usr) && usr.Id != guildUser.Id)
|
||||||
|
//{
|
||||||
|
// if (!usersWhoVoted.Add(guildUser.Id))
|
||||||
|
// return;
|
||||||
|
// votes.AddOrUpdate(input, 1, (key, old) => ++old);
|
||||||
|
// await channel.SendConfirmAsync("Acrophobia", $"{guildUser.Mention} cast their vote!").ConfigureAwait(false);
|
||||||
|
// await msg.DeleteAsync().ConfigureAwait(false);
|
||||||
|
// return;
|
||||||
|
//}
|
||||||
|
|
||||||
|
int num;
|
||||||
|
if (int.TryParse(input, out num) && num > 0 && num <= submissions.Count)
|
||||||
|
{
|
||||||
|
var kvp = submissions.Skip(num - 1).First();
|
||||||
|
usr = kvp.Value;
|
||||||
|
//can't vote for yourself, can't vote multiple times
|
||||||
|
if (usr.Id == guildUser.Id || !usersWhoVoted.Add(guildUser.Id))
|
||||||
|
return;
|
||||||
|
votes.AddOrUpdate(kvp.Key, 1, (key, old) => ++old);
|
||||||
|
await channel.SendConfirmAsync("Acrophobia", $"{guildUser.Mention} cast their vote!").ConfigureAwait(false);
|
||||||
|
await msg.DeleteAsync().ConfigureAwait(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_log.Warn(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task End()
|
||||||
|
{
|
||||||
|
if (!votes.Any())
|
||||||
|
{
|
||||||
|
await channel.SendErrorAsync("Acrophobia", "No votes cast. Game ended with no winner.").ConfigureAwait(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var table = votes.OrderByDescending(v => v.Value);
|
||||||
|
var winner = table.First();
|
||||||
|
var embed = new EmbedBuilder().WithOkColor()
|
||||||
|
.WithTitle("Acrophobia")
|
||||||
|
.WithDescription($"Winner is {submissions[winner.Key].Mention} with {winner.Value} points.\n")
|
||||||
|
.WithFooter(efb => efb.WithText(winner.Key.ToLowerInvariant().ToTitleCase()));
|
||||||
|
|
||||||
|
await channel.EmbedAsync(embed.Build()).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EnsureStopped()
|
||||||
|
{
|
||||||
|
NadekoBot.Client.MessageReceived -= PotentialAcro;
|
||||||
|
if (!source.IsCancellationRequested)
|
||||||
|
source.Cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,7 @@ using NadekoBot.Services;
|
|||||||
using NLog;
|
using NLog;
|
||||||
using Services.CleverBotApi;
|
using Services.CleverBotApi;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
@ -23,22 +24,25 @@ namespace NadekoBot.Modules.Games
|
|||||||
public string Status { get; set; }
|
public string Status { get; set; }
|
||||||
public string Response { get; set; }
|
public string Response { get; set; }
|
||||||
}
|
}
|
||||||
//user#discrim is the key
|
|
||||||
public static ConcurrentHashSet<string> ChannelsInConversation { get; } = new ConcurrentHashSet<string>();
|
public static ConcurrentDictionary<ulong, Lazy<ChatterBotSession>> CleverbotGuilds { get; } = new ConcurrentDictionary<ulong, Lazy<ChatterBotSession>>();
|
||||||
public static ConcurrentDictionary<ulong, ChatterBotSession> CleverbotGuilds { get; } = new ConcurrentDictionary<ulong, ChatterBotSession>();
|
|
||||||
|
|
||||||
static CleverBotCommands()
|
static CleverBotCommands()
|
||||||
{
|
{
|
||||||
_log = LogManager.GetCurrentClassLogger();
|
_log = LogManager.GetCurrentClassLogger();
|
||||||
|
var sw = Stopwatch.StartNew();
|
||||||
|
|
||||||
using (var uow = DbHandler.UnitOfWork())
|
using (var uow = DbHandler.UnitOfWork())
|
||||||
{
|
{
|
||||||
var bot = ChatterBotFactory.Create(ChatterBotType.CLEVERBOT);
|
var bot = ChatterBotFactory.Create(ChatterBotType.CLEVERBOT);
|
||||||
CleverbotGuilds = new ConcurrentDictionary<ulong, ChatterBotSession>(
|
CleverbotGuilds = new ConcurrentDictionary<ulong, Lazy<ChatterBotSession>>(
|
||||||
NadekoBot.AllGuildConfigs
|
NadekoBot.AllGuildConfigs
|
||||||
.Where(gc => gc.CleverbotEnabled)
|
.Where(gc => gc.CleverbotEnabled)
|
||||||
.ToDictionary(gc => gc.GuildId, gc => bot.CreateSession()));
|
.ToDictionary(gc => gc.GuildId, gc => new Lazy<ChatterBotSession>(() => bot.CreateSession(), true)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sw.Stop();
|
||||||
|
_log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<bool> TryAsk(IUserMessage msg)
|
public static async Task<bool> TryAsk(IUserMessage msg)
|
||||||
@ -48,7 +52,7 @@ namespace NadekoBot.Modules.Games
|
|||||||
if (channel == null)
|
if (channel == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
ChatterBotSession cleverbot;
|
Lazy<ChatterBotSession> cleverbot;
|
||||||
if (!CleverbotGuilds.TryGetValue(channel.Guild.Id, out cleverbot))
|
if (!CleverbotGuilds.TryGetValue(channel.Guild.Id, out cleverbot))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -71,7 +75,7 @@ namespace NadekoBot.Modules.Games
|
|||||||
|
|
||||||
await msg.Channel.TriggerTypingAsync().ConfigureAwait(false);
|
await msg.Channel.TriggerTypingAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
var response = await cleverbot.Think(message).ConfigureAwait(false);
|
var response = await cleverbot.Value.Think(message).ConfigureAwait(false);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await msg.Channel.SendConfirmAsync(response.SanitizeMentions()).ConfigureAwait(false);
|
await msg.Channel.SendConfirmAsync(response.SanitizeMentions()).ConfigureAwait(false);
|
||||||
@ -88,8 +92,8 @@ namespace NadekoBot.Modules.Games
|
|||||||
[RequireUserPermission(ChannelPermission.ManageMessages)]
|
[RequireUserPermission(ChannelPermission.ManageMessages)]
|
||||||
public async Task Cleverbot()
|
public async Task Cleverbot()
|
||||||
{
|
{
|
||||||
ChatterBotSession throwaway;
|
Lazy<ChatterBotSession> throwaway;
|
||||||
if (CleverbotGuilds.TryRemove(Context.Guild.Id, out throwaway))
|
if (CleverbotGuilds.TryRemove(channel.Guild.Id, out throwaway))
|
||||||
{
|
{
|
||||||
using (var uow = DbHandler.UnitOfWork())
|
using (var uow = DbHandler.UnitOfWork())
|
||||||
{
|
{
|
||||||
@ -101,9 +105,8 @@ namespace NadekoBot.Modules.Games
|
|||||||
}
|
}
|
||||||
|
|
||||||
var cleverbot = ChatterBotFactory.Create(ChatterBotType.CLEVERBOT);
|
var cleverbot = ChatterBotFactory.Create(ChatterBotType.CLEVERBOT);
|
||||||
var session = cleverbot.CreateSession();
|
|
||||||
|
|
||||||
CleverbotGuilds.TryAdd(Context.Guild.Id, session);
|
CleverbotGuilds.TryAdd(channel.Guild.Id, new Lazy<ChatterBotSession>(() => cleverbot.CreateSession(), true));
|
||||||
|
|
||||||
using (var uow = DbHandler.UnitOfWork())
|
using (var uow = DbHandler.UnitOfWork())
|
||||||
{
|
{
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
using NadekoBot.Extensions;
|
using NadekoBot.Extensions;
|
||||||
using NadekoBot.Services;
|
using NadekoBot.Services;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using NLog;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
@ -42,7 +43,8 @@ namespace NadekoBot.Modules.Games.Commands.Hangman
|
|||||||
.Concat(data.Things)
|
.Concat(data.Things)
|
||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
catch (Exception ex) {
|
catch (Exception ex)
|
||||||
|
{
|
||||||
Console.WriteLine(ex);
|
Console.WriteLine(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -69,6 +71,8 @@ namespace NadekoBot.Modules.Games.Commands.Hangman
|
|||||||
|
|
||||||
public class HangmanGame
|
public class HangmanGame
|
||||||
{
|
{
|
||||||
|
private readonly Logger _log;
|
||||||
|
|
||||||
public IMessageChannel GameChannel { get; }
|
public IMessageChannel GameChannel { get; }
|
||||||
public HashSet<char> Guesses { get; } = new HashSet<char>();
|
public HashSet<char> Guesses { get; } = new HashSet<char>();
|
||||||
public HangmanObject Term { get; private set; }
|
public HangmanObject Term { get; private set; }
|
||||||
@ -96,6 +100,7 @@ namespace NadekoBot.Modules.Games.Commands.Hangman
|
|||||||
|
|
||||||
public HangmanGame(IMessageChannel channel, HangmanTermPool.HangmanTermType type)
|
public HangmanGame(IMessageChannel channel, HangmanTermPool.HangmanTermType type)
|
||||||
{
|
{
|
||||||
|
_log = LogManager.GetCurrentClassLogger();
|
||||||
this.GameChannel = channel;
|
this.GameChannel = channel;
|
||||||
this.TermType = type;
|
this.TermType = type;
|
||||||
}
|
}
|
||||||
@ -115,44 +120,48 @@ namespace NadekoBot.Modules.Games.Commands.Hangman
|
|||||||
var embed = new EmbedBuilder().WithTitle("Hangman Game")
|
var embed = new EmbedBuilder().WithTitle("Hangman Game")
|
||||||
.WithDescription(toSend)
|
.WithDescription(toSend)
|
||||||
.AddField(efb => efb.WithName("It was").WithValue(Term.Word))
|
.AddField(efb => efb.WithName("It was").WithValue(Term.Word))
|
||||||
.WithImageUrl(Term.ImageUrl);
|
.WithImage(eib => eib.WithUrl(Term.ImageUrl))
|
||||||
|
.WithFooter(efb => efb.WithText(string.Join(" ", Guesses)));
|
||||||
if (Errors >= MaxErrors)
|
if (Errors >= MaxErrors)
|
||||||
await GameChannel.EmbedAsync(embed.WithColor(NadekoBot.ErrorColor)).ConfigureAwait(false);
|
await GameChannel.EmbedAsync(embed.WithErrorColor()).ConfigureAwait(false);
|
||||||
else
|
else
|
||||||
await GameChannel.EmbedAsync(embed.WithColor(NadekoBot.OkColor)).ConfigureAwait(false);
|
await GameChannel.EmbedAsync(embed.WithOkColor()).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task PotentialGuess(IMessage msg)
|
private async void PotentialGuess(IMessage msg)
|
||||||
{
|
{
|
||||||
if (msg.Channel != GameChannel)
|
try
|
||||||
return Task.CompletedTask; // message's channel has to be the same as game's
|
|
||||||
if (msg.Content.Length != 1) // message must be 1 char long
|
|
||||||
{
|
{
|
||||||
if (++MessagesSinceLastPost > 10)
|
if (!(msg is IUserMessage))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (msg.Channel != GameChannel)
|
||||||
|
return; // message's channel has to be the same as game's
|
||||||
|
if (msg.Content.Length == 1) // message must be 1 char long
|
||||||
{
|
{
|
||||||
MessagesSinceLastPost = 0;
|
if (++MessagesSinceLastPost > 10)
|
||||||
Task.Run(async () =>
|
|
||||||
{
|
{
|
||||||
try { await GameChannel.SendConfirmAsync("Hangman Game", ScrambledWord + "\n" + GetHangman()).ConfigureAwait(false); } catch { }
|
MessagesSinceLastPost = 0;
|
||||||
});
|
try
|
||||||
}
|
{
|
||||||
return Task.CompletedTask;
|
await GameChannel.SendConfirmAsync("Hangman Game",
|
||||||
}
|
ScrambledWord + "\n" + GetHangman(),
|
||||||
|
footer: string.Join(" ", Guesses)).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
|
||||||
if (!(char.IsLetter(msg.Content[0]) || char.IsDigit(msg.Content[0])))// and a letter or a digit
|
if (!(char.IsLetter(msg.Content[0]) || char.IsDigit(msg.Content[0])))// and a letter or a digit
|
||||||
return Task.CompletedTask;
|
return;
|
||||||
|
|
||||||
var guess = char.ToUpperInvariant(msg.Content[0]);
|
var guess = char.ToUpperInvariant(msg.Content[0]);
|
||||||
|
|
||||||
Task.Run(async () =>
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (Guesses.Contains(guess))
|
if (Guesses.Contains(guess))
|
||||||
{
|
{
|
||||||
|
MessagesSinceLastPost = 0;
|
||||||
++Errors;
|
++Errors;
|
||||||
if (Errors < MaxErrors)
|
if (Errors < MaxErrors)
|
||||||
await GameChannel.SendErrorAsync("Hangman Game", $"{msg.Author.Mention} Letter `{guess}` has already been used.\n" + ScrambledWord + "\n" + GetHangman()).ConfigureAwait(false);
|
await GameChannel.SendErrorAsync("Hangman Game", $"{msg.Author.Mention} Letter `{guess}` has already been used.\n" + ScrambledWord + "\n" + GetHangman(),
|
||||||
|
footer: string.Join(" ", Guesses)).ConfigureAwait(false);
|
||||||
else
|
else
|
||||||
await End().ConfigureAwait(false);
|
await End().ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
@ -166,26 +175,32 @@ namespace NadekoBot.Modules.Games.Commands.Hangman
|
|||||||
{
|
{
|
||||||
try { await GameChannel.SendConfirmAsync("Hangman Game", $"{msg.Author.Mention} guessed a letter `{guess}`!").ConfigureAwait(false); } catch { }
|
try { await GameChannel.SendConfirmAsync("Hangman Game", $"{msg.Author.Mention} guessed a letter `{guess}`!").ConfigureAwait(false); } catch { }
|
||||||
|
|
||||||
await End().ConfigureAwait(false);
|
await End().ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try { await GameChannel.SendConfirmAsync("Hangman Game", $"{msg.Author.Mention} guessed a letter `{guess}`!\n" + ScrambledWord + "\n" + GetHangman()).ConfigureAwait(false); } catch { }
|
MessagesSinceLastPost = 0;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await GameChannel.SendConfirmAsync("Hangman Game", $"{msg.Author.Mention} guessed a letter `{guess}`!\n" + ScrambledWord + "\n" + GetHangman(),
|
||||||
|
footer: string.Join(" ", Guesses)).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
MessagesSinceLastPost = 0;
|
||||||
++Errors;
|
++Errors;
|
||||||
if (Errors < MaxErrors)
|
if (Errors < MaxErrors)
|
||||||
await GameChannel.SendErrorAsync("Hangman Game", $"{msg.Author.Mention} Letter `{guess}` does not exist.\n" + ScrambledWord + "\n" + GetHangman()).ConfigureAwait(false);
|
await GameChannel.SendErrorAsync("Hangman Game", $"{msg.Author.Mention} Letter `{guess}` does not exist.\n" + ScrambledWord + "\n" + GetHangman(),
|
||||||
|
footer: string.Join(" ", Guesses)).ConfigureAwait(false);
|
||||||
else
|
else
|
||||||
await End().ConfigureAwait(false);
|
await End().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
catch { }
|
}
|
||||||
|
catch (Exception ex) { _log.Warn(ex); }
|
||||||
});
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetHangman() => $@"\_\_\_\_\_\_\_\_\_
|
public string GetHangman() => $@"\_\_\_\_\_\_\_\_\_
|
||||||
|
@ -9,6 +9,7 @@ using NLog;
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
@ -34,6 +35,8 @@ namespace NadekoBot.Modules.Games
|
|||||||
//channelId/last generation
|
//channelId/last generation
|
||||||
private static ConcurrentDictionary<ulong, DateTime> lastGenerations { get; } = new ConcurrentDictionary<ulong, DateTime>();
|
private static ConcurrentDictionary<ulong, DateTime> lastGenerations { get; } = new ConcurrentDictionary<ulong, DateTime>();
|
||||||
|
|
||||||
|
private static ConcurrentHashSet<ulong> usersRecentlyPicked { get; } = new ConcurrentHashSet<ulong>();
|
||||||
|
|
||||||
private static float chance { get; }
|
private static float chance { get; }
|
||||||
private static int cooldown { get; }
|
private static int cooldown { get; }
|
||||||
private static Logger _log { get; }
|
private static Logger _log { get; }
|
||||||
@ -41,6 +44,8 @@ namespace NadekoBot.Modules.Games
|
|||||||
static PlantPickCommands()
|
static PlantPickCommands()
|
||||||
{
|
{
|
||||||
_log = LogManager.GetCurrentClassLogger();
|
_log = LogManager.GetCurrentClassLogger();
|
||||||
|
var sw = Stopwatch.StartNew();
|
||||||
|
|
||||||
NadekoBot.Client.MessageReceived += PotentialFlowerGeneration;
|
NadekoBot.Client.MessageReceived += PotentialFlowerGeneration;
|
||||||
|
|
||||||
using (var uow = DbHandler.UnitOfWork())
|
using (var uow = DbHandler.UnitOfWork())
|
||||||
@ -48,27 +53,30 @@ namespace NadekoBot.Modules.Games
|
|||||||
var conf = uow.BotConfig.GetOrCreate();
|
var conf = uow.BotConfig.GetOrCreate();
|
||||||
var x =
|
var x =
|
||||||
generationChannels = new ConcurrentHashSet<ulong>(NadekoBot.AllGuildConfigs
|
generationChannels = new ConcurrentHashSet<ulong>(NadekoBot.AllGuildConfigs
|
||||||
.SelectMany(c => c.GenerateCurrencyChannelIds.Select(obj=>obj.ChannelId)));
|
.SelectMany(c => c.GenerateCurrencyChannelIds.Select(obj => obj.ChannelId)));
|
||||||
chance = conf.CurrencyGenerationChance;
|
chance = conf.CurrencyGenerationChance;
|
||||||
cooldown = conf.CurrencyGenerationCooldown;
|
cooldown = conf.CurrencyGenerationCooldown;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sw.Stop();
|
||||||
|
_log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Task PotentialFlowerGeneration(IMessage imsg)
|
private static async void PotentialFlowerGeneration(IMessage imsg)
|
||||||
{
|
{
|
||||||
var msg = imsg as IUserMessage;
|
try
|
||||||
if (msg == null || msg.IsAuthor() || imsg.Author.IsBot)
|
|
||||||
return Task.CompletedTask;
|
|
||||||
|
|
||||||
var channel = imsg.Channel as ITextChannel;
|
|
||||||
if (channel == null)
|
|
||||||
return Task.CompletedTask;
|
|
||||||
|
|
||||||
if (!generationChannels.Contains(channel.Id))
|
|
||||||
return Task.CompletedTask;
|
|
||||||
|
|
||||||
var t = Task.Run(async () =>
|
|
||||||
{
|
{
|
||||||
|
var msg = imsg as IUserMessage;
|
||||||
|
if (msg == null || msg.IsAuthor() || msg.Author.IsBot)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var channel = imsg.Channel as ITextChannel;
|
||||||
|
if (channel == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!generationChannels.Contains(channel.Id))
|
||||||
|
return;
|
||||||
|
|
||||||
var lastGeneration = lastGenerations.GetOrAdd(channel.Id, DateTime.MinValue);
|
var lastGeneration = lastGenerations.GetOrAdd(channel.Id, DateTime.MinValue);
|
||||||
var rng = new NadekoRandom();
|
var rng = new NadekoRandom();
|
||||||
|
|
||||||
@ -80,47 +88,48 @@ namespace NadekoBot.Modules.Games
|
|||||||
if (num > 100)
|
if (num > 100)
|
||||||
{
|
{
|
||||||
lastGenerations.AddOrUpdate(channel.Id, DateTime.Now, (id, old) => DateTime.Now);
|
lastGenerations.AddOrUpdate(channel.Id, DateTime.Now, (id, old) => DateTime.Now);
|
||||||
try
|
|
||||||
{
|
var sent = await channel.SendFileAsync(
|
||||||
var sent = await channel.SendFileAsync(
|
GetRandomCurrencyImagePath(),
|
||||||
GetRandomCurrencyImagePath(),
|
$"❗ A random { Gambling.Gambling.CurrencyName } appeared! Pick it up by typing `{NadekoBot.ModulePrefixes[typeof(Games).Name]}pick`")
|
||||||
$"❗ A random { Gambling.Gambling.CurrencyName } appeared! Pick it up by typing `{NadekoBot.ModulePrefixes[typeof(Games).Name]}pick`")
|
.ConfigureAwait(false);
|
||||||
.ConfigureAwait(false);
|
plantedFlowers.AddOrUpdate(channel.Id, new List<IUserMessage>() { sent }, (id, old) => { old.Add(sent); return old; });
|
||||||
plantedFlowers.AddOrUpdate(channel.Id, new List<IUserMessage>() { sent }, (id, old) => { old.Add(sent); return old; });
|
|
||||||
}
|
|
||||||
catch { }
|
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
return Task.CompletedTask;
|
catch { }
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
public async Task Pick()
|
public async Task Pick()
|
||||||
{
|
{
|
||||||
var channel = (ITextChannel)Context.Channel;
|
var channel = (ITextChannel)Context.Channel;
|
||||||
|
|
||||||
if (!(await channel.Guild.GetCurrentUserAsync()).GetPermissions(channel).ManageMessages)
|
if (!channel.Guild.GetCurrentUser().GetPermissions(channel).ManageMessages || !usersRecentlyPicked.Add(imsg.Author.Id))
|
||||||
{
|
|
||||||
await channel.SendErrorAsync("I need manage channel permissions in order to process this command.").ConfigureAwait(false);
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
List<IUserMessage> msgs;
|
try
|
||||||
|
{
|
||||||
|
|
||||||
|
List<IUserMessage> msgs;
|
||||||
|
|
||||||
try { await Context.Message.DeleteAsync().ConfigureAwait(false); } catch { }
|
try { await Context.Message.DeleteAsync().ConfigureAwait(false); } catch { }
|
||||||
if (!plantedFlowers.TryRemove(channel.Id, out msgs))
|
if (!plantedFlowers.TryRemove(channel.Id, out msgs))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
await Task.WhenAll(msgs.Select(toDelete => toDelete.DeleteAsync())).ConfigureAwait(false);
|
await Task.WhenAll(msgs.Select(toDelete => toDelete.DeleteAsync())).ConfigureAwait(false);
|
||||||
|
|
||||||
await CurrencyHandler.AddCurrencyAsync((IGuildUser)Context.User, "Picked flower(s).", msgs.Count, false).ConfigureAwait(false);
|
await CurrencyHandler.AddCurrencyAsync((IGuildUser)imsg.Author, "Picked flower(s).", msgs.Count, false).ConfigureAwait(false);
|
||||||
var msg = await channel.SendConfirmAsync($"**{Context.User}** picked {msgs.Count}{Gambling.Gambling.CurrencySign}!").ConfigureAwait(false);
|
var msg = await channel.SendConfirmAsync($"**{imsg.Author}** picked {msgs.Count}{Gambling.Gambling.CurrencySign}!").ConfigureAwait(false);
|
||||||
var t = Task.Run(async () =>
|
msg.DeleteAfter(10);
|
||||||
|
}
|
||||||
|
finally
|
||||||
{
|
{
|
||||||
await Task.Delay(10000).ConfigureAwait(false);
|
await Task.Delay(60000);
|
||||||
try { await msg.DeleteAsync().ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
|
usersRecentlyPicked.TryRemove(imsg.Author.Id);
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
|
@ -128,63 +128,57 @@ namespace NadekoBot.Modules.Games
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task Vote(SocketMessage imsg)
|
private async void Vote(IMessage imsg)
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
// has to be a user message
|
// has to be a user message
|
||||||
var msg = imsg as SocketUserMessage;
|
var msg = imsg as IUserMessage;
|
||||||
if (msg == null || imsg.Author.IsBot)
|
if (msg == null || msg.Author.IsBot)
|
||||||
return Task.CompletedTask;
|
return;
|
||||||
|
|
||||||
// has to be an integer
|
// has to be an integer
|
||||||
int vote;
|
int vote;
|
||||||
if (!int.TryParse(imsg.Content, out vote))
|
if (!int.TryParse(imsg.Content, out vote))
|
||||||
return Task.CompletedTask;
|
return;
|
||||||
if (vote < 1 || vote > answers.Length)
|
if (vote < 1 || vote > answers.Length)
|
||||||
return Task.CompletedTask;
|
return;
|
||||||
|
|
||||||
var t = Task.Run(async () =>
|
IMessageChannel ch;
|
||||||
|
if (isPublic)
|
||||||
{
|
{
|
||||||
try
|
//if public, channel must be the same the poll started in
|
||||||
|
if (originalMessage.Channel.Id != imsg.Channel.Id)
|
||||||
|
return;
|
||||||
|
ch = imsg.Channel;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//if private, channel must be dm channel
|
||||||
|
if ((ch = msg.Channel as IDMChannel) == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// user must be a member of the guild this poll is in
|
||||||
|
var guildUsers = await guild.GetUsersAsync().ConfigureAwait(false);
|
||||||
|
if (!guildUsers.Any(u => u.Id == imsg.Author.Id))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//user can vote only once
|
||||||
|
if (participants.TryAdd(msg.Author.Id, vote))
|
||||||
|
{
|
||||||
|
if (!isPublic)
|
||||||
{
|
{
|
||||||
IMessageChannel ch;
|
await ch.SendConfirmAsync($"Thanks for voting **{msg.Author.Username}**.").ConfigureAwait(false);
|
||||||
if (isPublic)
|
|
||||||
{
|
|
||||||
//if public, channel must be the same the poll started in
|
|
||||||
if (originalMessage.Channel.Id != imsg.Channel.Id)
|
|
||||||
return;
|
|
||||||
ch = imsg.Channel;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//if private, channel must be dm channel
|
|
||||||
if ((ch = msg.Channel as SocketDMChannel) == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// user must be a member of the guild this poll is in
|
|
||||||
var guildUsers = await guild.GetUsersAsync().ConfigureAwait(false);
|
|
||||||
if (!guildUsers.Any(u => u.Id == msg.Author.Id))
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//user can vote only once
|
|
||||||
if (participants.TryAdd(msg.Author.Id, vote))
|
|
||||||
{
|
|
||||||
if (!isPublic)
|
|
||||||
{
|
|
||||||
await ch.SendConfirmAsync($"Thanks for voting **{msg.Author.Username}**.").ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var toDelete = await ch.SendConfirmAsync($"{msg.Author.Mention} cast their vote.").ConfigureAwait(false);
|
|
||||||
await Task.Delay(5000);
|
|
||||||
await toDelete.DeleteAsync().ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch { }
|
else
|
||||||
});
|
{
|
||||||
return Task.CompletedTask;
|
var toDelete = await ch.SendConfirmAsync($"{msg.Author.Mention} cast their vote.").ConfigureAwait(false);
|
||||||
|
toDelete.DeleteAfter(5);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
catch { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -105,42 +105,39 @@ namespace NadekoBot.Modules.Games
|
|||||||
NadekoBot.Client.MessageReceived += AnswerReceived;
|
NadekoBot.Client.MessageReceived += AnswerReceived;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task AnswerReceived(IMessage imsg)
|
private async void AnswerReceived(IMessage imsg)
|
||||||
{
|
{
|
||||||
if (imsg.Author.IsBot)
|
try
|
||||||
return Task.CompletedTask;
|
|
||||||
var msg = imsg as IUserMessage;
|
|
||||||
if (msg == null)
|
|
||||||
return Task.CompletedTask;
|
|
||||||
var t = Task.Run(async () =>
|
|
||||||
{
|
{
|
||||||
try
|
if (imsg.Author.IsBot)
|
||||||
|
return;
|
||||||
|
var msg = imsg as IUserMessage;
|
||||||
|
if (msg == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (this.Channel == null || this.Channel.Id != this.Channel.Id) return;
|
||||||
|
|
||||||
|
var guess = msg.Content;
|
||||||
|
|
||||||
|
var distance = CurrentSentence.LevenshteinDistance(guess);
|
||||||
|
var decision = Judge(distance, guess.Length);
|
||||||
|
if (decision && !finishedUserIds.Contains(msg.Author.Id))
|
||||||
{
|
{
|
||||||
if (this.Channel == null || this.Channel.Id != this.Channel.Id) return;
|
var wpm = CurrentSentence.Length / WORD_VALUE / sw.Elapsed.Seconds * 60;
|
||||||
|
finishedUserIds.Add(msg.Author.Id);
|
||||||
var guess = msg.Content;
|
await Extensions.Extensions.EmbedAsync(this.Channel, (Discord.API.Embed)new EmbedBuilder().WithColor((uint)NadekoBot.OkColor)
|
||||||
|
.WithTitle((string)$"{msg.Author} finished the race!")
|
||||||
var distance = CurrentSentence.LevenshteinDistance(guess);
|
.AddField(efb => efb.WithName("Place").WithValue($"#{finishedUserIds.Count}").WithIsInline(true))
|
||||||
var decision = Judge(distance, guess.Length);
|
.AddField(efb => efb.WithName("WPM").WithValue($"{wpm:F2} *[{sw.Elapsed.Seconds.ToString()}sec]*").WithIsInline(true))
|
||||||
if (decision && !finishedUserIds.Contains(imsg.Author.Id))
|
.AddField(efb => efb.WithName((string)"Errors").WithValue((string)distance.ToString()).WithIsInline((bool)true))
|
||||||
|
.Build()).ConfigureAwait(false);
|
||||||
|
if (finishedUserIds.Count % 4 == 0)
|
||||||
{
|
{
|
||||||
var wpm = CurrentSentence.Length / WORD_VALUE / sw.Elapsed.Seconds * 60;
|
await this.Channel.SendConfirmAsync($":exclamation: A lot of people finished, here is the text for those still typing:\n\n**{Format.Sanitize(CurrentSentence.Replace(" ", " \x200B")).SanitizeMentions()}**").ConfigureAwait(false);
|
||||||
finishedUserIds.Add(imsg.Author.Id);
|
|
||||||
await Extensions.Extensions.EmbedAsync(this.Channel, new EmbedBuilder().WithColor(NadekoBot.OkColor)
|
|
||||||
.WithTitle((string)$"{imsg.Author} finished the race!")
|
|
||||||
.AddField(efb => efb.WithName("Place").WithValue($"#{finishedUserIds.Count}").WithIsInline(true))
|
|
||||||
.AddField(efb => efb.WithName("WPM").WithValue($"{wpm:F2} *[{sw.Elapsed.Seconds.ToString()}sec]*").WithIsInline(true))
|
|
||||||
.AddField(efb => efb.WithName((string)"Errors").WithValue((string)distance.ToString()).WithIsInline((bool)true))
|
|
||||||
).ConfigureAwait(false);
|
|
||||||
if (finishedUserIds.Count % 4 == 0)
|
|
||||||
{
|
|
||||||
await Extensions.Extensions.SendConfirmAsync(this.Channel, (string)$":exclamation: A lot of people finished, here is the text for those still typing:\n\n**{Format.Sanitize((string)CurrentSentence.Replace((string)" ", (string)" \x200B")).SanitizeMentions()}**").ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch { }
|
}
|
||||||
});
|
catch (Exception ex) { _log.Warn(ex); }
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool Judge(int errors, int textLength) => errors <= textLength / 25;
|
private bool Judge(int errors, int textLength) => errors <= textLength / 25;
|
||||||
|
@ -22,7 +22,7 @@ namespace NadekoBot.Modules.Games.Trivia
|
|||||||
|
|
||||||
private int QuestionDurationMiliseconds { get; } = 30000;
|
private int QuestionDurationMiliseconds { get; } = 30000;
|
||||||
private int HintTimeoutMiliseconds { get; } = 6000;
|
private int HintTimeoutMiliseconds { get; } = 6000;
|
||||||
public bool ShowHints { get; set; } = true;
|
public bool ShowHints { get; } = true;
|
||||||
private CancellationTokenSource triviaCancelSource { get; set; }
|
private CancellationTokenSource triviaCancelSource { get; set; }
|
||||||
|
|
||||||
public TriviaQuestion CurrentQuestion { get; private set; }
|
public TriviaQuestion CurrentQuestion { get; private set; }
|
||||||
@ -35,141 +35,161 @@ namespace NadekoBot.Modules.Games.Trivia
|
|||||||
|
|
||||||
public int WinRequirement { get; } = 10;
|
public int WinRequirement { get; } = 10;
|
||||||
|
|
||||||
public TriviaGame(IGuild guild, ITextChannel channel, bool showHints, int winReq = 10)
|
public TriviaGame(IGuild guild, ITextChannel channel, bool showHints, int winReq)
|
||||||
{
|
{
|
||||||
_log = LogManager.GetCurrentClassLogger();
|
this._log = LogManager.GetCurrentClassLogger();
|
||||||
ShowHints = showHints;
|
|
||||||
|
this.ShowHints = showHints;
|
||||||
this.guild = guild;
|
this.guild = guild;
|
||||||
this.channel = channel;
|
this.channel = channel;
|
||||||
WinRequirement = winReq;
|
this.WinRequirement = winReq;
|
||||||
Task.Run(async () => { try { await StartGame().ConfigureAwait(false); } catch { } });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task StartGame()
|
public async Task StartGame()
|
||||||
{
|
{
|
||||||
while (!ShouldStopGame)
|
while (!ShouldStopGame)
|
||||||
{
|
{
|
||||||
// reset the cancellation source
|
// reset the cancellation source
|
||||||
triviaCancelSource = new CancellationTokenSource();
|
triviaCancelSource = new CancellationTokenSource();
|
||||||
var token = triviaCancelSource.Token;
|
|
||||||
// load question
|
// load question
|
||||||
CurrentQuestion = TriviaQuestionPool.Instance.GetRandomQuestion(oldQuestions);
|
CurrentQuestion = TriviaQuestionPool.Instance.GetRandomQuestion(oldQuestions);
|
||||||
if (CurrentQuestion == null)
|
if (CurrentQuestion == null)
|
||||||
{
|
{
|
||||||
try { await channel.SendErrorAsync($":exclamation: Failed loading a trivia question.").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
|
await channel.SendErrorAsync("Trivia Game", "Failed loading a question.").ConfigureAwait(false);
|
||||||
await End().ConfigureAwait(false);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
oldQuestions.Add(CurrentQuestion); //add it to exclusion list so it doesn't show up again
|
oldQuestions.Add(CurrentQuestion); //add it to exclusion list so it doesn't show up again
|
||||||
//sendquestion
|
|
||||||
try { await channel.SendConfirmAsync($":question: Question",$"**{CurrentQuestion.Question}**").ConfigureAwait(false); }
|
|
||||||
catch (HttpException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound || ex.StatusCode == System.Net.HttpStatusCode.Forbidden)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
catch (Exception ex) { _log.Warn(ex); }
|
|
||||||
|
|
||||||
//receive messages
|
|
||||||
NadekoBot.Client.MessageReceived += PotentialGuess;
|
|
||||||
|
|
||||||
//allow people to guess
|
|
||||||
GameActive = true;
|
|
||||||
|
|
||||||
|
EmbedBuilder questionEmbed;
|
||||||
|
IUserMessage questionMessage;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
//hint
|
questionEmbed = new EmbedBuilder().WithOkColor()
|
||||||
await Task.Delay(HintTimeoutMiliseconds, token).ConfigureAwait(false);
|
.WithTitle("Trivia Game")
|
||||||
if (ShowHints)
|
.AddField(eab => eab.WithName("Category").WithValue(CurrentQuestion.Category))
|
||||||
try { await channel.SendConfirmAsync($":exclamation: Hint", CurrentQuestion.GetHint()).ConfigureAwait(false); }
|
.AddField(eab => eab.WithName("Question").WithValue(CurrentQuestion.Question));
|
||||||
catch (HttpException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound || ex.StatusCode == System.Net.HttpStatusCode.Forbidden)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
catch (Exception ex) { _log.Warn(ex); }
|
|
||||||
|
|
||||||
//timeout
|
|
||||||
await Task.Delay(QuestionDurationMiliseconds - HintTimeoutMiliseconds, token).ConfigureAwait(false);
|
|
||||||
|
|
||||||
|
questionMessage = await channel.EmbedAsync(questionEmbed.Build()).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch (HttpException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound || ex.StatusCode == System.Net.HttpStatusCode.Forbidden)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_log.Warn(ex);
|
||||||
|
await Task.Delay(2000).ConfigureAwait(false);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//receive messages
|
||||||
|
try
|
||||||
|
{
|
||||||
|
NadekoBot.Client.MessageReceived += PotentialGuess;
|
||||||
|
|
||||||
|
//allow people to guess
|
||||||
|
GameActive = true;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
//hint
|
||||||
|
await Task.Delay(HintTimeoutMiliseconds, triviaCancelSource.Token).ConfigureAwait(false);
|
||||||
|
if (ShowHints)
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await questionMessage.ModifyAsync(m => m.Embed = questionEmbed.WithFooter(efb => efb.WithText(CurrentQuestion.GetHint())).Build())
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch (HttpException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound || ex.StatusCode == System.Net.HttpStatusCode.Forbidden)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
catch (Exception ex) { _log.Warn(ex); }
|
||||||
|
|
||||||
|
//timeout
|
||||||
|
await Task.Delay(QuestionDurationMiliseconds - HintTimeoutMiliseconds, triviaCancelSource.Token).ConfigureAwait(false);
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (TaskCanceledException) { } //means someone guessed the answer
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
GameActive = false;
|
||||||
|
NadekoBot.Client.MessageReceived -= PotentialGuess;
|
||||||
}
|
}
|
||||||
catch (TaskCanceledException) { } //means someone guessed the answer
|
|
||||||
GameActive = false;
|
|
||||||
if (!triviaCancelSource.IsCancellationRequested)
|
if (!triviaCancelSource.IsCancellationRequested)
|
||||||
try { await channel.SendConfirmAsync($":clock2: :question: **Time's up!** The correct answer was **{CurrentQuestion.Answer}**").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
|
try { await channel.SendErrorAsync("Trivia Game", $"**Time's up!** The correct answer was **{CurrentQuestion.Answer}**").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
|
||||||
NadekoBot.Client.MessageReceived -= PotentialGuess;
|
|
||||||
// load next question if game is still running
|
|
||||||
await Task.Delay(2000).ConfigureAwait(false);
|
await Task.Delay(2000).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
try { NadekoBot.Client.MessageReceived -= PotentialGuess; } catch { }
|
|
||||||
GameActive = false;
|
|
||||||
await End().ConfigureAwait(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task End()
|
public async Task EnsureStopped()
|
||||||
{
|
{
|
||||||
ShouldStopGame = true;
|
ShouldStopGame = true;
|
||||||
TriviaGame throwaway;
|
|
||||||
Games.TriviaCommands.RunningTrivias.TryRemove(channel.Guild.Id, out throwaway);
|
await channel.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||||
try
|
.WithAuthor(eab => eab.WithName("Trivia Game Ended"))
|
||||||
{
|
.WithTitle("Final Results")
|
||||||
await channel.EmbedAsync(new EmbedBuilder().WithColor(NadekoBot.OkColor)
|
.WithDescription(GetLeaderboard())
|
||||||
.WithTitle("Leaderboard")
|
.Build()).ConfigureAwait(false);
|
||||||
.WithDescription(GetLeaderboard())
|
|
||||||
, "Trivia game ended.").ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
catch { }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task StopGame()
|
public async Task StopGame()
|
||||||
{
|
{
|
||||||
if (!ShouldStopGame)
|
var old = ShouldStopGame;
|
||||||
try { await channel.SendConfirmAsync(":exclamation: Trivia will stop after this question.").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
|
|
||||||
ShouldStopGame = true;
|
ShouldStopGame = true;
|
||||||
|
if (!old)
|
||||||
|
try { await channel.SendConfirmAsync("Trivia Game", "Stopping after this question.").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task PotentialGuess(IMessage imsg)
|
private async void PotentialGuess(IMessage imsg)
|
||||||
{
|
{
|
||||||
if (imsg.Author.IsBot)
|
try
|
||||||
return Task.CompletedTask;
|
|
||||||
var umsg = imsg as IUserMessage;
|
|
||||||
if (umsg == null)
|
|
||||||
return Task.CompletedTask;
|
|
||||||
var t = Task.Run(async () =>
|
|
||||||
{
|
{
|
||||||
|
if (imsg.Author.IsBot)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var umsg = imsg as IUserMessage;
|
||||||
|
if (umsg == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var textChannel = umsg.Channel as ITextChannel;
|
||||||
|
if (textChannel == null || textChannel.Guild != guild)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var guildUser = (IGuildUser)umsg.Author;
|
||||||
|
|
||||||
|
var guess = false;
|
||||||
|
await _guessLock.WaitAsync().ConfigureAwait(false);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!(imsg.Channel is IGuildChannel && imsg.Channel is ITextChannel)) return;
|
if (GameActive && CurrentQuestion.IsAnswerCorrect(umsg.Content) && !triviaCancelSource.IsCancellationRequested)
|
||||||
if ((imsg.Channel as ITextChannel).Guild != guild) return;
|
|
||||||
if (imsg.Author.Id == NadekoBot.Client.CurrentUser().Id) return;
|
|
||||||
|
|
||||||
var guildUser = imsg.Author as IGuildUser;
|
|
||||||
|
|
||||||
var guess = false;
|
|
||||||
await _guessLock.WaitAsync().ConfigureAwait(false);
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
if (GameActive && CurrentQuestion.IsAnswerCorrect(umsg.Content) && !triviaCancelSource.IsCancellationRequested)
|
Users.AddOrUpdate(guildUser, 1, (gu, old) => ++old);
|
||||||
{
|
guess = true;
|
||||||
Users.AddOrUpdate(guildUser, 1, (gu, old) => ++old);
|
|
||||||
guess = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
finally { _guessLock.Release(); }
|
|
||||||
if (!guess) return;
|
|
||||||
triviaCancelSource.Cancel();
|
|
||||||
try { await channel.SendConfirmAsync($"☑️ {guildUser.Mention} guessed it! The answer was: **{CurrentQuestion.Answer}**").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
|
|
||||||
if (Users[guildUser] != WinRequirement) return;
|
|
||||||
ShouldStopGame = true;
|
|
||||||
await channel.SendConfirmAsync($":exclamation: We have a winner! It's {guildUser.Mention}.").ConfigureAwait(false);
|
|
||||||
}
|
}
|
||||||
catch (Exception ex) { _log.Warn(ex); }
|
finally { _guessLock.Release(); }
|
||||||
});
|
if (!guess) return;
|
||||||
return Task.CompletedTask;
|
triviaCancelSource.Cancel();
|
||||||
|
|
||||||
|
|
||||||
|
if (Users[guildUser] == WinRequirement)
|
||||||
|
{
|
||||||
|
ShouldStopGame = true;
|
||||||
|
await channel.SendConfirmAsync("Trivia Game", $"{guildUser.Mention} guessed it and WON the game! The answer was: **{CurrentQuestion.Answer}**").ConfigureAwait(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await channel.SendConfirmAsync("Trivia Game", $"{guildUser.Mention} guessed it! The answer was: **{CurrentQuestion.Answer}**").ConfigureAwait(false);
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception ex) { _log.Warn(ex); }
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetLeaderboard()
|
public string GetLeaderboard()
|
||||||
{
|
{
|
||||||
if (Users.Count == 0)
|
if (Users.Count == 0)
|
||||||
return "";
|
return "No results.";
|
||||||
|
|
||||||
var sb = new StringBuilder();
|
var sb = new StringBuilder();
|
||||||
|
|
||||||
|
@ -18,9 +18,9 @@ namespace NadekoBot.Modules.Games.Trivia
|
|||||||
};
|
};
|
||||||
public static int maxStringLength = 22;
|
public static int maxStringLength = 22;
|
||||||
|
|
||||||
public string Category;
|
public string Category { get; set; }
|
||||||
public string Question;
|
public string Question { get; set; }
|
||||||
public string Answer;
|
public string Answer { get; set; }
|
||||||
|
|
||||||
public TriviaQuestion(string q, string a, string c)
|
public TriviaQuestion(string q, string a, string c)
|
||||||
{
|
{
|
||||||
@ -79,12 +79,9 @@ namespace NadekoBot.Modules.Games.Trivia
|
|||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string ToString() =>
|
|
||||||
"Question: **" + this.Question + "?**";
|
|
||||||
|
|
||||||
private static string Scramble(string word)
|
private static string Scramble(string word)
|
||||||
{
|
{
|
||||||
var letters = word.ToArray();
|
var letters = word.ToCharArray();
|
||||||
var count = 0;
|
var count = 0;
|
||||||
for (var i = 0; i < letters.Length; i++)
|
for (var i = 0; i < letters.Length; i++)
|
||||||
{
|
{
|
||||||
@ -101,7 +98,7 @@ namespace NadekoBot.Modules.Games.Trivia
|
|||||||
if (letters[i] != ' ')
|
if (letters[i] != ' ')
|
||||||
letters[i] = '_';
|
letters[i] = '_';
|
||||||
}
|
}
|
||||||
return "`" + string.Join(" ", letters) + "`";
|
return string.Join(" \x200B", new string(letters).Replace(" ", " \x200B").AsEnumerable());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using NadekoBot.Extensions;
|
using NadekoBot.Extensions;
|
||||||
using NadekoBot.Services;
|
using NadekoBot.Services;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
@ -11,36 +12,31 @@ namespace NadekoBot.Modules.Games.Trivia
|
|||||||
{
|
{
|
||||||
public class TriviaQuestionPool
|
public class TriviaQuestionPool
|
||||||
{
|
{
|
||||||
public static TriviaQuestionPool Instance { get; } = new TriviaQuestionPool();
|
private static TriviaQuestionPool _instance;
|
||||||
public ConcurrentHashSet<TriviaQuestion> pool = new ConcurrentHashSet<TriviaQuestion>();
|
public static TriviaQuestionPool Instance { get; } = _instance ?? (_instance = new TriviaQuestionPool());
|
||||||
|
|
||||||
|
private const string questionsFile = "data/trivia_questions.json";
|
||||||
|
|
||||||
private Random rng { get; } = new NadekoRandom();
|
private Random rng { get; } = new NadekoRandom();
|
||||||
|
|
||||||
|
private TriviaQuestion[] pool { get; }
|
||||||
|
|
||||||
static TriviaQuestionPool() { }
|
static TriviaQuestionPool() { }
|
||||||
|
|
||||||
private TriviaQuestionPool()
|
private TriviaQuestionPool()
|
||||||
{
|
{
|
||||||
Reload();
|
pool = JsonConvert.DeserializeObject<TriviaQuestion[]>(File.ReadAllText(questionsFile));
|
||||||
}
|
}
|
||||||
|
|
||||||
public TriviaQuestion GetRandomQuestion(IEnumerable<TriviaQuestion> exclude)
|
public TriviaQuestion GetRandomQuestion(HashSet<TriviaQuestion> exclude)
|
||||||
{
|
{
|
||||||
var list = pool.Except(exclude).ToList();
|
if (pool.Length == 0)
|
||||||
var rand = rng.Next(0, list.Count);
|
return null;
|
||||||
return list[rand];
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Reload()
|
TriviaQuestion randomQuestion;
|
||||||
{
|
while (exclude.Contains(randomQuestion = pool[rng.Next(0, pool.Length)])) ;
|
||||||
var arr = JArray.Parse(File.ReadAllText("data/questions.json"));
|
|
||||||
|
|
||||||
foreach (var item in arr)
|
return randomQuestion;
|
||||||
{
|
|
||||||
var tq = new TriviaQuestion(item["Question"].ToString().SanitizeMentions(), item["Answer"].ToString().SanitizeMentions(), item["Category"]?.ToString());
|
|
||||||
pool.Add(tq);
|
|
||||||
}
|
|
||||||
var r = new NadekoRandom();
|
|
||||||
pool = new ConcurrentHashSet<TriviaQuestion>(pool.OrderBy(x => r.Next()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,24 +20,30 @@ namespace NadekoBot.Modules.Games
|
|||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
public async Task Trivia(params string[] args)
|
public Task Trivia([Remainder] string additionalArgs = "")
|
||||||
|
=> Trivia(Context.Message, 10, additionalArgs);
|
||||||
|
|
||||||
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
public async Task Trivia(IUserMessage umsg, int winReq = 10, [Remainder] string additionalArgs = "")
|
||||||
{
|
{
|
||||||
TriviaGame trivia;
|
var channel = (ITextChannel)umsg.Channel;
|
||||||
if (!RunningTrivias.TryGetValue(Context.Guild.Id, out trivia))
|
|
||||||
|
var showHints = !additionalArgs.Contains("nohint");
|
||||||
|
|
||||||
|
TriviaGame trivia = new TriviaGame(channel.Guild, channel, showHints, winReq);
|
||||||
|
if (RunningTrivias.TryAdd(channel.Guild.Id, trivia))
|
||||||
{
|
{
|
||||||
var showHints = !args.Contains("nohint");
|
try
|
||||||
var number = args.Select(s =>
|
|
||||||
{
|
{
|
||||||
int num;
|
await trivia.StartGame().ConfigureAwait(false);
|
||||||
return new Tuple<bool, int>(int.TryParse(s, out num), num);
|
}
|
||||||
}).Where(t => t.Item1).Select(t => t.Item2).FirstOrDefault();
|
finally
|
||||||
if (number < 0)
|
{
|
||||||
return;
|
RunningTrivias.TryRemove(channel.Guild.Id, out trivia);
|
||||||
var triviaGame = new TriviaGame(Context.Guild, (ITextChannel)Context.Channel, showHints, number == 0 ? 10 : number);
|
await trivia.EnsureStopped().ConfigureAwait(false);
|
||||||
if (RunningTrivias.TryAdd(Context.Guild.Id, triviaGame))
|
}
|
||||||
await Context.Channel.SendConfirmAsync($"**Trivia game started! {triviaGame.WinRequirement} points needed to win.**").ConfigureAwait(false);
|
return;
|
||||||
else
|
|
||||||
await triviaGame.StopGame().ConfigureAwait(false);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
await Context.Channel.SendErrorAsync("Trivia game is already running on this server.\n" + trivia.CurrentQuestion).ConfigureAwait(false);
|
await Context.Channel.SendErrorAsync("Trivia game is already running on this server.\n" + trivia.CurrentQuestion).ConfigureAwait(false);
|
||||||
@ -51,9 +57,12 @@ namespace NadekoBot.Modules.Games
|
|||||||
|
|
||||||
TriviaGame trivia;
|
TriviaGame trivia;
|
||||||
if (RunningTrivias.TryGetValue(channel.Guild.Id, out trivia))
|
if (RunningTrivias.TryGetValue(channel.Guild.Id, out trivia))
|
||||||
|
{
|
||||||
await channel.SendConfirmAsync("Leaderboard", trivia.GetLeaderboard()).ConfigureAwait(false);
|
await channel.SendConfirmAsync("Leaderboard", trivia.GetLeaderboard()).ConfigureAwait(false);
|
||||||
else
|
return;
|
||||||
await channel.SendErrorAsync("No trivia is running on this server.").ConfigureAwait(false);
|
}
|
||||||
|
|
||||||
|
await channel.SendErrorAsync("No trivia is running on this server.").ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
@ -66,9 +75,10 @@ namespace NadekoBot.Modules.Games
|
|||||||
if (RunningTrivias.TryGetValue(channel.Guild.Id, out trivia))
|
if (RunningTrivias.TryGetValue(channel.Guild.Id, out trivia))
|
||||||
{
|
{
|
||||||
await trivia.StopGame().ConfigureAwait(false);
|
await trivia.StopGame().ConfigureAwait(false);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
await channel.SendErrorAsync("No trivia is running on this server.").ConfigureAwait(false);
|
await channel.SendErrorAsync("No trivia is running on this server.").ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,9 +21,7 @@ namespace NadekoBot.Modules.Games
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public Games() : base()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
|
@ -22,6 +22,8 @@ namespace NadekoBot.Modules.Help
|
|||||||
|
|
||||||
static Help()
|
static Help()
|
||||||
{
|
{
|
||||||
|
|
||||||
|
//todo don't cache this, just query db when someone wants -h
|
||||||
using (var uow = DbHandler.UnitOfWork())
|
using (var uow = DbHandler.UnitOfWork())
|
||||||
{
|
{
|
||||||
var config = uow.BotConfig.GetOrCreate();
|
var config = uow.BotConfig.GetOrCreate();
|
||||||
@ -38,10 +40,10 @@ namespace NadekoBot.Modules.Help
|
|||||||
public async Task Modules()
|
public async Task Modules()
|
||||||
{
|
{
|
||||||
|
|
||||||
var mdls = NadekoBot.CommandService.Modules.GroupBy(mi => mi.GetTopLevelModule()).Select(m => m.Key.Name).OrderBy(m => m);
|
var embed = new EmbedBuilder().WithOkColor().WithFooter(efb => efb.WithText($" ℹ️ Type `-cmds ModuleName` to get a list of commands in that module. eg `-cmds games`"))
|
||||||
await Context.Channel.SendMessageAsync("📜 **List of modules:** ```css\n• " + string.Join("\n• ",
|
.WithTitle("📜 List Of Modules").WithDescription("\n• " + string.Join("\n• ", NadekoBot.CommandService.Modules.Select(m => m.Name).OrderBy(s=>s)))
|
||||||
mdls) + $"\n``` ℹ️ **Type** `-commands module_name` **to get a list of commands in that module.** ***e.g.*** `-commands games`")
|
.Build();
|
||||||
.ConfigureAwait(false);
|
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
|
@ -30,6 +30,26 @@ namespace NadekoBot.Modules.Music.Classes
|
|||||||
{
|
{
|
||||||
private IAudioClient audioClient { get; set; }
|
private IAudioClient audioClient { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Player will prioritize different queuer name
|
||||||
|
/// over the song position in the playlist
|
||||||
|
/// </summary>
|
||||||
|
public bool FairPlay { get; set; } = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Song will stop playing after this amount of time.
|
||||||
|
/// To prevent people queueing radio or looped songs
|
||||||
|
/// while other people want to listen to other songs too.
|
||||||
|
/// </summary>
|
||||||
|
public uint MaxPlaytimeSeconds { get; set; } = 0;
|
||||||
|
|
||||||
|
public TimeSpan TotalPlaytime => new TimeSpan(playlist.Sum(s => s.TotalTime.Ticks));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Users who recently got their music wish
|
||||||
|
/// </summary>
|
||||||
|
private ConcurrentHashSet<string> recentlyPlayedUsers { get; } = new ConcurrentHashSet<string>();
|
||||||
|
|
||||||
private readonly List<Song> playlist = new List<Song>();
|
private readonly List<Song> playlist = new List<Song>();
|
||||||
public IReadOnlyCollection<Song> Playlist => playlist;
|
public IReadOnlyCollection<Song> Playlist => playlist;
|
||||||
|
|
||||||
@ -41,8 +61,8 @@ namespace NadekoBot.Modules.Music.Classes
|
|||||||
|
|
||||||
public float Volume { get; private set; }
|
public float Volume { get; private set; }
|
||||||
|
|
||||||
public event EventHandler<Song> OnCompleted = delegate { };
|
public event Action<MusicPlayer, Song> OnCompleted = delegate { };
|
||||||
public event EventHandler<Song> OnStarted = delegate { };
|
public event Action<MusicPlayer, Song> OnStarted = delegate { };
|
||||||
public event Action<bool> OnPauseChanged = delegate { };
|
public event Action<bool> OnPauseChanged = delegate { };
|
||||||
|
|
||||||
public IVoiceChannel PlaybackVoiceChannel { get; private set; }
|
public IVoiceChannel PlaybackVoiceChannel { get; private set; }
|
||||||
@ -53,7 +73,9 @@ namespace NadekoBot.Modules.Music.Classes
|
|||||||
public bool Autoplay { get; set; } = false;
|
public bool Autoplay { get; set; } = false;
|
||||||
public uint MaxQueueSize { get; set; } = 0;
|
public uint MaxQueueSize { get; set; } = 0;
|
||||||
|
|
||||||
private ConcurrentQueue<Action> actionQueue { get; set; } = new ConcurrentQueue<Action>();
|
private ConcurrentQueue<Action> actionQueue { get; } = new ConcurrentQueue<Action>();
|
||||||
|
|
||||||
|
public string PrettyVolume => $"🔉 {(int)(Volume * 100)}%";
|
||||||
|
|
||||||
public MusicPlayer(IVoiceChannel startingVoiceChannel, float? defaultVolume)
|
public MusicPlayer(IVoiceChannel startingVoiceChannel, float? defaultVolume)
|
||||||
{
|
{
|
||||||
@ -107,11 +129,13 @@ namespace NadekoBot.Modules.Music.Classes
|
|||||||
}
|
}
|
||||||
|
|
||||||
CurrentSong = GetNextSong();
|
CurrentSong = GetNextSong();
|
||||||
RemoveSongAt(0);
|
|
||||||
|
|
||||||
if (CurrentSong == null)
|
if (CurrentSong == null)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
var index = playlist.IndexOf(CurrentSong);
|
||||||
|
if (index != -1)
|
||||||
|
RemoveSongAt(index);
|
||||||
|
|
||||||
OnStarted(this, CurrentSong);
|
OnStarted(this, CurrentSong);
|
||||||
await CurrentSong.Play(audioClient, cancelToken);
|
await CurrentSong.Play(audioClient, cancelToken);
|
||||||
@ -130,7 +154,7 @@ namespace NadekoBot.Modules.Music.Classes
|
|||||||
{
|
{
|
||||||
Console.WriteLine("Music thread almost crashed.");
|
Console.WriteLine("Music thread almost crashed.");
|
||||||
Console.WriteLine(ex);
|
Console.WriteLine(ex);
|
||||||
await Task.Delay(30000);
|
await Task.Delay(3000).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@ -183,8 +207,26 @@ namespace NadekoBot.Modules.Music.Classes
|
|||||||
return volume;
|
return volume;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Song GetNextSong() =>
|
private Song GetNextSong()
|
||||||
playlist.FirstOrDefault();
|
{
|
||||||
|
if (!FairPlay)
|
||||||
|
{
|
||||||
|
return playlist.FirstOrDefault();
|
||||||
|
}
|
||||||
|
var song = playlist.FirstOrDefault(c => !recentlyPlayedUsers.Contains(c.QueuerName))
|
||||||
|
?? playlist.FirstOrDefault();
|
||||||
|
|
||||||
|
if (song == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (recentlyPlayedUsers.Contains(song.QueuerName))
|
||||||
|
{
|
||||||
|
recentlyPlayedUsers.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
recentlyPlayedUsers.Add(song.QueuerName);
|
||||||
|
return song;
|
||||||
|
}
|
||||||
|
|
||||||
public void Shuffle()
|
public void Shuffle()
|
||||||
{
|
{
|
||||||
@ -251,11 +293,11 @@ namespace NadekoBot.Modules.Music.Classes
|
|||||||
{
|
{
|
||||||
var curSong = CurrentSong;
|
var curSong = CurrentSong;
|
||||||
var toUpdate = playlist.Where(s => s.SongInfo.ProviderType == MusicType.Normal &&
|
var toUpdate = playlist.Where(s => s.SongInfo.ProviderType == MusicType.Normal &&
|
||||||
s.TotalLength == TimeSpan.Zero);
|
s.TotalTime == TimeSpan.Zero);
|
||||||
if (curSong != null)
|
if (curSong != null)
|
||||||
toUpdate = toUpdate.Append(curSong);
|
toUpdate = toUpdate.Append(curSong);
|
||||||
var ids = toUpdate.Select(s => s.SongInfo.Query.Substring(s.SongInfo.Query.LastIndexOf("?v=") + 3))
|
var ids = toUpdate.Select(s => s.SongInfo.Query.Substring(s.SongInfo.Query.LastIndexOf("?v=") + 3))
|
||||||
.Distinct();
|
.Distinct();
|
||||||
|
|
||||||
var durations = await NadekoBot.Google.GetVideoDurationsAsync(ids);
|
var durations = await NadekoBot.Google.GetVideoDurationsAsync(ids);
|
||||||
|
|
||||||
@ -265,11 +307,12 @@ namespace NadekoBot.Modules.Music.Classes
|
|||||||
{
|
{
|
||||||
if (s.SongInfo.Query.EndsWith(kvp.Key))
|
if (s.SongInfo.Query.EndsWith(kvp.Key))
|
||||||
{
|
{
|
||||||
s.TotalLength = kvp.Value;
|
s.TotalTime = kvp.Value;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Destroy()
|
public void Destroy()
|
||||||
|
15
src/NadekoBot/Modules/Music/Classes/MusicExtensions.cs
Normal file
15
src/NadekoBot/Modules/Music/Classes/MusicExtensions.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
using Discord;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace NadekoBot.Modules.Music.Classes
|
||||||
|
{
|
||||||
|
public static class MusicExtensions
|
||||||
|
{
|
||||||
|
public static EmbedAuthorBuilder WithMusicIcon(this EmbedAuthorBuilder eab) =>
|
||||||
|
eab.WithIconUrl("http://i.imgur.com/nhKS3PT.png");
|
||||||
|
}
|
||||||
|
}
|
@ -11,6 +11,7 @@ using System.Text.RegularExpressions;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using VideoLibrary;
|
using VideoLibrary;
|
||||||
|
using System.Net;
|
||||||
|
|
||||||
namespace NadekoBot.Modules.Music.Classes
|
namespace NadekoBot.Modules.Music.Classes
|
||||||
{
|
{
|
||||||
@ -18,42 +19,20 @@ namespace NadekoBot.Modules.Music.Classes
|
|||||||
{
|
{
|
||||||
public string Provider { get; set; }
|
public string Provider { get; set; }
|
||||||
public MusicType ProviderType { get; set; }
|
public MusicType ProviderType { get; set; }
|
||||||
/// <summary>
|
|
||||||
/// Will be set only if the providertype is normal
|
|
||||||
/// </summary>
|
|
||||||
public string Query { get; set; }
|
public string Query { get; set; }
|
||||||
public string Title { get; set; }
|
public string Title { get; set; }
|
||||||
public string Uri { get; set; }
|
public string Uri { get; set; }
|
||||||
public string AlbumArt { get; set; }
|
public string AlbumArt { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Song
|
public class Song
|
||||||
{
|
{
|
||||||
public StreamState State { get; set; }
|
|
||||||
public string PrettyName =>
|
|
||||||
$"**{SongInfo.Title.TrimTo(55)} `{(SongInfo.Provider ?? "-")} by {QueuerName}`**";
|
|
||||||
//$"{SongInfo.Title.TrimTo(70)}";
|
|
||||||
public SongInfo SongInfo { get; }
|
public SongInfo SongInfo { get; }
|
||||||
public MusicPlayer MusicPlayer { get; set; }
|
public MusicPlayer MusicPlayer { get; set; }
|
||||||
|
|
||||||
public string PrettyUser =>
|
|
||||||
$"{QueuerName}";
|
|
||||||
public string QueuerName { get; set; }
|
public string QueuerName { get; set; }
|
||||||
|
|
||||||
public string PrettyProvider =>
|
public TimeSpan TotalTime { get; set; } = TimeSpan.Zero;
|
||||||
$"{(SongInfo.Provider ?? "No Provider")}";
|
public TimeSpan CurrentTime => TimeSpan.FromSeconds(bytesSent / frameBytes / (1000 / milliseconds));
|
||||||
|
|
||||||
public string PrettyCurrentTime()
|
|
||||||
{
|
|
||||||
var time = TimeSpan.FromSeconds(bytesSent / 3840 / 50);
|
|
||||||
var str = $"{(int)time.TotalMinutes}m {time.Seconds}s / ";
|
|
||||||
if (TotalLength == TimeSpan.Zero)
|
|
||||||
str += "**?**";
|
|
||||||
else if (TotalLength == TimeSpan.MaxValue)
|
|
||||||
str += "**∞**";
|
|
||||||
else
|
|
||||||
str += $"{(int)TotalLength.TotalMinutes}m {TotalLength.Seconds}s";
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
const int milliseconds = 20;
|
const int milliseconds = 20;
|
||||||
const int samplesPerFrame = (48000 / 1000) * milliseconds;
|
const int samplesPerFrame = (48000 / 1000) * milliseconds;
|
||||||
@ -61,11 +40,71 @@ namespace NadekoBot.Modules.Music.Classes
|
|||||||
|
|
||||||
private ulong bytesSent { get; set; } = 0;
|
private ulong bytesSent { get; set; } = 0;
|
||||||
|
|
||||||
public bool PrintStatusMessage { get; set; } = true;
|
//pwetty
|
||||||
|
|
||||||
|
public string PrettyProvider =>
|
||||||
|
$"{(SongInfo.Provider ?? "No Provider")}";
|
||||||
|
|
||||||
|
public string PrettyFullTime => PrettyCurrentTime + " / " + PrettyTotalTime;
|
||||||
|
|
||||||
|
public string PrettyName => $"**[{SongInfo.Title.TrimTo(65)}]({songUrl})**";
|
||||||
|
|
||||||
|
public string PrettyInfo => $"{MusicPlayer.PrettyVolume} | {PrettyTotalTime} | {PrettyProvider} | {QueuerName}";
|
||||||
|
|
||||||
|
public string PrettyFullName => $"{PrettyName}\n\t\t`{PrettyTotalTime} | {PrettyProvider} | {QueuerName}`";
|
||||||
|
|
||||||
|
public string PrettyCurrentTime => CurrentTime.ToString(@"mm\:ss");
|
||||||
|
|
||||||
|
private string PrettyTotalTime {
|
||||||
|
get {
|
||||||
|
if (TotalTime == TimeSpan.Zero)
|
||||||
|
return "(?)";
|
||||||
|
else if (TotalTime == TimeSpan.MaxValue)
|
||||||
|
return "∞";
|
||||||
|
else
|
||||||
|
return TotalTime.ToString(@"mm\:ss");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Thumbnail {
|
||||||
|
get {
|
||||||
|
switch (SongInfo.ProviderType)
|
||||||
|
{
|
||||||
|
case MusicType.Radio:
|
||||||
|
return $"https://cdn.discordapp.com/attachments/155726317222887425/261850925063340032/1482522097_radio.png"; //test links
|
||||||
|
case MusicType.Normal:
|
||||||
|
//todo have videoid in songinfo from the start
|
||||||
|
var videoId = Regex.Match(SongInfo.Query, "<=v=[a-zA-Z0-9-]+(?=&)|(?<=[0-9])[^&\n]+|(?<=v=)[^&\n]+");
|
||||||
|
return $"https://img.youtube.com/vi/{ videoId }/0.jpg";
|
||||||
|
case MusicType.Local:
|
||||||
|
return $"https://cdn.discordapp.com/attachments/155726317222887425/261850914783100928/1482522077_music.png"; //test links
|
||||||
|
case MusicType.Soundcloud:
|
||||||
|
return SongInfo.AlbumArt;
|
||||||
|
default:
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string songUrl {
|
||||||
|
get {
|
||||||
|
switch (SongInfo.ProviderType)
|
||||||
|
{
|
||||||
|
case MusicType.Normal:
|
||||||
|
return SongInfo.Query;
|
||||||
|
case MusicType.Soundcloud:
|
||||||
|
return SongInfo.Query;
|
||||||
|
case MusicType.Local:
|
||||||
|
return $"https://google.com/search?q={ WebUtility.UrlEncode(SongInfo.Title).Replace(' ', '+') }";
|
||||||
|
case MusicType.Radio:
|
||||||
|
return $"https://google.com/search?q={SongInfo.Title}";
|
||||||
|
default:
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private int skipTo = 0;
|
private int skipTo = 0;
|
||||||
private Logger _log;
|
|
||||||
|
|
||||||
public int SkipTo {
|
public int SkipTo {
|
||||||
get { return skipTo; }
|
get { return skipTo; }
|
||||||
set {
|
set {
|
||||||
@ -74,7 +113,7 @@ namespace NadekoBot.Modules.Music.Classes
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public TimeSpan TotalLength { get; set; } = TimeSpan.Zero;
|
private readonly Logger _log;
|
||||||
|
|
||||||
public Song(SongInfo songInfo)
|
public Song(SongInfo songInfo)
|
||||||
{
|
{
|
||||||
@ -86,16 +125,10 @@ namespace NadekoBot.Modules.Music.Classes
|
|||||||
{
|
{
|
||||||
var s = new Song(SongInfo);
|
var s = new Song(SongInfo);
|
||||||
s.MusicPlayer = MusicPlayer;
|
s.MusicPlayer = MusicPlayer;
|
||||||
s.State = StreamState.Queued;
|
s.QueuerName = QueuerName;
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Song SetMusicPlayer(MusicPlayer mp)
|
|
||||||
{
|
|
||||||
this.MusicPlayer = mp;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task Play(IAudioClient voiceClient, CancellationToken cancelToken)
|
public async Task Play(IAudioClient voiceClient, CancellationToken cancelToken)
|
||||||
{
|
{
|
||||||
var filename = Path.Combine(Music.MusicDataPath, DateTime.Now.UnixTimestamp().ToString());
|
var filename = Path.Combine(Music.MusicDataPath, DateTime.Now.UnixTimestamp().ToString());
|
||||||
@ -136,7 +169,7 @@ namespace NadekoBot.Modules.Music.Classes
|
|||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (prebufferingTask.IsCanceled)
|
else if (prebufferingTask.IsCanceled)
|
||||||
{
|
{
|
||||||
_log.Debug("Prebuffering canceled. Cannot get any data from the stream.");
|
_log.Debug("Prebuffering canceled. Cannot get any data from the stream.");
|
||||||
@ -145,19 +178,20 @@ namespace NadekoBot.Modules.Music.Classes
|
|||||||
finished = true;
|
finished = true;
|
||||||
}
|
}
|
||||||
sw.Stop();
|
sw.Stop();
|
||||||
_log.Debug("Prebuffering successfully completed in "+ sw.Elapsed);
|
_log.Debug("Prebuffering successfully completed in " + sw.Elapsed);
|
||||||
|
|
||||||
var outStream = voiceClient.CreatePCMStream(960);
|
var outStream = voiceClient.CreatePCMStream(960);
|
||||||
|
|
||||||
int nextTime = Environment.TickCount + milliseconds;
|
int nextTime = Environment.TickCount + milliseconds;
|
||||||
|
|
||||||
byte[] buffer = new byte[frameBytes];
|
byte[] buffer = new byte[frameBytes];
|
||||||
while (!cancelToken.IsCancellationRequested)
|
while (!cancelToken.IsCancellationRequested && //song canceled for whatever reason
|
||||||
|
!(MusicPlayer.MaxPlaytimeSeconds != 0 && CurrentTime.TotalSeconds >= MusicPlayer.MaxPlaytimeSeconds)) // or exceedded max playtime
|
||||||
{
|
{
|
||||||
//Console.WriteLine($"Read: {songBuffer.ReadPosition}\nWrite: {songBuffer.WritePosition}\nContentLength:{songBuffer.ContentLength}\n---------");
|
//Console.WriteLine($"Read: {songBuffer.ReadPosition}\nWrite: {songBuffer.WritePosition}\nContentLength:{songBuffer.ContentLength}\n---------");
|
||||||
var read = await inStream.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false);
|
var read = await inStream.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false);
|
||||||
//await inStream.CopyToAsync(voiceClient.OutputStream);
|
//await inStream.CopyToAsync(voiceClient.OutputStream);
|
||||||
if(read < frameBytes)
|
if (read < frameBytes)
|
||||||
_log.Debug("read {0}", read);
|
_log.Debug("read {0}", read);
|
||||||
unchecked
|
unchecked
|
||||||
{
|
{
|
||||||
@ -177,10 +211,15 @@ namespace NadekoBot.Modules.Music.Classes
|
|||||||
if (slowconnection)
|
if (slowconnection)
|
||||||
{
|
{
|
||||||
_log.Warn("Slow connection has disrupted music, waiting a bit for buffer");
|
_log.Warn("Slow connection has disrupted music, waiting a bit for buffer");
|
||||||
|
|
||||||
await Task.Delay(1000, cancelToken).ConfigureAwait(false);
|
await Task.Delay(1000, cancelToken).ConfigureAwait(false);
|
||||||
|
nextTime = Environment.TickCount + milliseconds;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
await Task.Delay(100, cancelToken).ConfigureAwait(false);
|
await Task.Delay(100, cancelToken).ConfigureAwait(false);
|
||||||
|
nextTime = Environment.TickCount + milliseconds;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
attempt = 0;
|
attempt = 0;
|
||||||
@ -189,7 +228,10 @@ namespace NadekoBot.Modules.Music.Classes
|
|||||||
attempt = 0;
|
attempt = 0;
|
||||||
|
|
||||||
while (this.MusicPlayer.Paused)
|
while (this.MusicPlayer.Paused)
|
||||||
|
{
|
||||||
await Task.Delay(200, cancelToken).ConfigureAwait(false);
|
await Task.Delay(200, cancelToken).ConfigureAwait(false);
|
||||||
|
nextTime = Environment.TickCount + milliseconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
buffer = AdjustVolume(buffer, MusicPlayer.Volume);
|
buffer = AdjustVolume(buffer, MusicPlayer.Volume);
|
||||||
@ -204,7 +246,7 @@ namespace NadekoBot.Modules.Music.Classes
|
|||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
await bufferTask;
|
await bufferTask;
|
||||||
if(inStream != null)
|
if (inStream != null)
|
||||||
inStream.Dispose();
|
inStream.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -218,35 +260,6 @@ namespace NadekoBot.Modules.Music.Classes
|
|||||||
_log.Debug("Buffering successfull");
|
_log.Debug("Buffering successfull");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
//stackoverflow ftw
|
|
||||||
private static byte[] AdjustVolume(byte[] audioSamples, float volume)
|
|
||||||
{
|
|
||||||
if (Math.Abs(volume - 1.0f) < 0.01f)
|
|
||||||
return audioSamples;
|
|
||||||
var array = new byte[audioSamples.Length];
|
|
||||||
for (var i = 0; i < array.Length; i += 2)
|
|
||||||
{
|
|
||||||
|
|
||||||
// convert byte pair to int
|
|
||||||
short buf1 = audioSamples[i + 1];
|
|
||||||
short buf2 = audioSamples[i];
|
|
||||||
|
|
||||||
buf1 = (short)((buf1 & 0xff) << 8);
|
|
||||||
buf2 = (short)(buf2 & 0xff);
|
|
||||||
|
|
||||||
var res = (short)(buf1 | buf2);
|
|
||||||
res = (short)(res * volume);
|
|
||||||
|
|
||||||
// convert back
|
|
||||||
array[i] = (byte)res;
|
|
||||||
array[i + 1] = (byte)(res >> 8);
|
|
||||||
|
|
||||||
}
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
//aidiakapi ftw
|
//aidiakapi ftw
|
||||||
public unsafe static byte[] AdjustVolume(byte[] audioSamples, float volume)
|
public unsafe static byte[] AdjustVolume(byte[] audioSamples, float volume)
|
||||||
{
|
{
|
||||||
@ -272,202 +285,5 @@ namespace NadekoBot.Modules.Music.Classes
|
|||||||
|
|
||||||
return audioSamples;
|
return audioSamples;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<Song> ResolveSong(string query, MusicType musicType = MusicType.Normal)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(query))
|
|
||||||
throw new ArgumentNullException(nameof(query));
|
|
||||||
|
|
||||||
if (musicType != MusicType.Local && IsRadioLink(query))
|
|
||||||
{
|
|
||||||
musicType = MusicType.Radio;
|
|
||||||
query = await HandleStreamContainers(query).ConfigureAwait(false) ?? query;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
switch (musicType)
|
|
||||||
{
|
|
||||||
case MusicType.Local:
|
|
||||||
return new Song(new SongInfo
|
|
||||||
{
|
|
||||||
Uri = "\"" + Path.GetFullPath(query) + "\"",
|
|
||||||
Title = Path.GetFileNameWithoutExtension(query),
|
|
||||||
Provider = "Local File",
|
|
||||||
ProviderType = musicType,
|
|
||||||
Query = query,
|
|
||||||
});
|
|
||||||
case MusicType.Radio:
|
|
||||||
return new Song(new SongInfo
|
|
||||||
{
|
|
||||||
Uri = query,
|
|
||||||
Title = $"{query}",
|
|
||||||
Provider = "Radio Stream",
|
|
||||||
ProviderType = musicType,
|
|
||||||
Query = query
|
|
||||||
})
|
|
||||||
{ TotalLength = TimeSpan.MaxValue };
|
|
||||||
}
|
|
||||||
if (SoundCloud.Default.IsSoundCloudLink(query))
|
|
||||||
{
|
|
||||||
var svideo = await SoundCloud.Default.ResolveVideoAsync(query).ConfigureAwait(false);
|
|
||||||
return new Song(new SongInfo
|
|
||||||
{
|
|
||||||
Title = svideo.FullName,
|
|
||||||
Provider = "SoundCloud",
|
|
||||||
Uri = svideo.StreamLink,
|
|
||||||
ProviderType = musicType,
|
|
||||||
Query = svideo.TrackLink,
|
|
||||||
AlbumArt = svideo.artwork_url,
|
|
||||||
})
|
|
||||||
{ TotalLength = TimeSpan.FromMilliseconds(svideo.Duration) };
|
|
||||||
}
|
|
||||||
|
|
||||||
if (musicType == MusicType.Soundcloud)
|
|
||||||
{
|
|
||||||
var svideo = await SoundCloud.Default.GetVideoByQueryAsync(query).ConfigureAwait(false);
|
|
||||||
return new Song(new SongInfo
|
|
||||||
{
|
|
||||||
Title = svideo.FullName,
|
|
||||||
Provider = "SoundCloud",
|
|
||||||
Uri = svideo.StreamLink,
|
|
||||||
ProviderType = MusicType.Normal,
|
|
||||||
Query = svideo.TrackLink,
|
|
||||||
AlbumArt = svideo.artwork_url,
|
|
||||||
})
|
|
||||||
{ TotalLength = TimeSpan.FromMilliseconds(svideo.Duration) };
|
|
||||||
}
|
|
||||||
|
|
||||||
var link = (await NadekoBot.Google.GetVideosByKeywordsAsync(query).ConfigureAwait(false)).FirstOrDefault();
|
|
||||||
if (string.IsNullOrWhiteSpace(link))
|
|
||||||
throw new OperationCanceledException("Not a valid youtube query.");
|
|
||||||
var allVideos = await Task.Run(async () => { try { return await YouTube.Default.GetAllVideosAsync(link).ConfigureAwait(false); } catch { return Enumerable.Empty<YouTubeVideo>(); } }).ConfigureAwait(false);
|
|
||||||
var videos = allVideos.Where(v => v.AdaptiveKind == AdaptiveKind.Audio);
|
|
||||||
var video = videos
|
|
||||||
.Where(v => v.AudioBitrate < 256)
|
|
||||||
.OrderByDescending(v => v.AudioBitrate)
|
|
||||||
.FirstOrDefault();
|
|
||||||
|
|
||||||
if (video == null) // do something with this error
|
|
||||||
throw new Exception("Could not load any video elements based on the query.");
|
|
||||||
var m = Regex.Match(query, @"\?t=(?<t>\d*)");
|
|
||||||
int gotoTime = 0;
|
|
||||||
if (m.Captures.Count > 0)
|
|
||||||
int.TryParse(m.Groups["t"].ToString(), out gotoTime);
|
|
||||||
var song = new Song(new SongInfo
|
|
||||||
{
|
|
||||||
Title = video.Title.Substring(0, video.Title.Length - 10), // removing trailing "- You Tube"
|
|
||||||
Provider = "YouTube",
|
|
||||||
Uri = video.Uri,
|
|
||||||
Query = link,
|
|
||||||
ProviderType = musicType,
|
|
||||||
});
|
|
||||||
song.SkipTo = gotoTime;
|
|
||||||
return song;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Console.WriteLine($"Failed resolving the link.{ex.Message}");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task<string> HandleStreamContainers(string query)
|
|
||||||
{
|
|
||||||
string file = null;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using (var http = new HttpClient())
|
|
||||||
{
|
|
||||||
file = await http.GetStringAsync(query).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return query;
|
|
||||||
}
|
|
||||||
if (query.Contains(".pls"))
|
|
||||||
{
|
|
||||||
//File1=http://armitunes.com:8000/
|
|
||||||
//Regex.Match(query)
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var m = Regex.Match(file, "File1=(?<url>.*?)\\n");
|
|
||||||
var res = m.Groups["url"]?.ToString();
|
|
||||||
return res?.Trim();
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
Console.WriteLine($"Failed reading .pls:\n{file}");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (query.Contains(".m3u"))
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
# This is a comment
|
|
||||||
C:\xxx4xx\xxxxxx3x\xx2xxxx\xx.mp3
|
|
||||||
C:\xxx5xx\x6xxxxxx\x7xxxxx\xx.mp3
|
|
||||||
*/
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var m = Regex.Match(file, "(?<url>^[^#].*)", RegexOptions.Multiline);
|
|
||||||
var res = m.Groups["url"]?.ToString();
|
|
||||||
return res?.Trim();
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
Console.WriteLine($"Failed reading .m3u:\n{file}");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
if (query.Contains(".asx"))
|
|
||||||
{
|
|
||||||
//<ref href="http://armitunes.com:8000"/>
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var m = Regex.Match(file, "<ref href=\"(?<url>.*?)\"");
|
|
||||||
var res = m.Groups["url"]?.ToString();
|
|
||||||
return res?.Trim();
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
Console.WriteLine($"Failed reading .asx:\n{file}");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (query.Contains(".xspf"))
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<playlist version="1" xmlns="http://xspf.org/ns/0/">
|
|
||||||
<trackList>
|
|
||||||
<track><location>file:///mp3s/song_1.mp3</location></track>
|
|
||||||
*/
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var m = Regex.Match(file, "<location>(?<url>.*?)</location>");
|
|
||||||
var res = m.Groups["url"]?.ToString();
|
|
||||||
return res?.Trim();
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
Console.WriteLine($"Failed reading .xspf:\n{file}");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return query;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool IsRadioLink(string query) =>
|
|
||||||
(query.StartsWith("http") ||
|
|
||||||
query.StartsWith("ww"))
|
|
||||||
&&
|
|
||||||
(query.Contains(".pls") ||
|
|
||||||
query.Contains(".m3u") ||
|
|
||||||
query.Contains(".asx") ||
|
|
||||||
query.Contains(".xspf"));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -25,21 +25,21 @@ namespace NadekoBot.Modules.Music.Classes
|
|||||||
_log = LogManager.GetCurrentClassLogger();
|
_log = LogManager.GetCurrentClassLogger();
|
||||||
}
|
}
|
||||||
|
|
||||||
MusicPlayer MusicPlayer;
|
MusicPlayer MusicPlayer { get; }
|
||||||
|
|
||||||
private string Basename;
|
private string Basename { get; }
|
||||||
|
|
||||||
private SongInfo SongInfo;
|
private SongInfo SongInfo { get; }
|
||||||
|
|
||||||
private int SkipTo;
|
private int SkipTo { get; }
|
||||||
|
|
||||||
private int MaxFileSize = 2.MiB();
|
private int MaxFileSize { get; } = 2.MiB();
|
||||||
|
|
||||||
private long FileNumber = -1;
|
private long FileNumber = -1;
|
||||||
|
|
||||||
private long NextFileToRead = 0;
|
private long NextFileToRead = 0;
|
||||||
|
|
||||||
public bool BufferingCompleted { get; private set;} = false;
|
public bool BufferingCompleted { get; private set; } = false;
|
||||||
|
|
||||||
private ulong CurrentBufferSize = 0;
|
private ulong CurrentBufferSize = 0;
|
||||||
|
|
||||||
@ -76,7 +76,8 @@ namespace NadekoBot.Modules.Music.Classes
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
outStream.Dispose();
|
outStream.Dispose();
|
||||||
}catch { }
|
}
|
||||||
|
catch { }
|
||||||
outStream = new FileStream(Basename + "-" + ++FileNumber, FileMode.Append, FileAccess.Write, FileShare.Read);
|
outStream = new FileStream(Basename + "-" + ++FileNumber, FileMode.Append, FileAccess.Write, FileShare.Read);
|
||||||
currentFileSize = bytesRead;
|
currentFileSize = bytesRead;
|
||||||
}
|
}
|
||||||
@ -108,8 +109,8 @@ Check the guides for your platform on how to setup ffmpeg correctly:
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
if(outStream != null)
|
if (outStream != null)
|
||||||
outStream.Dispose();
|
outStream.Dispose();
|
||||||
Console.WriteLine($"Buffering done.");
|
Console.WriteLine($"Buffering done.");
|
||||||
if (p != null)
|
if (p != null)
|
||||||
{
|
{
|
||||||
@ -151,7 +152,7 @@ Check the guides for your platform on how to setup ffmpeg correctly:
|
|||||||
|
|
||||||
private void CleanFiles()
|
private void CleanFiles()
|
||||||
{
|
{
|
||||||
for (long i = NextFileToRead - 1 ; i <= FileNumber; i++)
|
for (long i = NextFileToRead - 1; i <= FileNumber; i++)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -169,7 +170,7 @@ Check the guides for your platform on how to setup ffmpeg correctly:
|
|||||||
|
|
||||||
public override bool CanWrite => false;
|
public override bool CanWrite => false;
|
||||||
|
|
||||||
public override long Length => (long) CurrentBufferSize;
|
public override long Length => (long)CurrentBufferSize;
|
||||||
|
|
||||||
public override long Position { get; set; } = 0;
|
public override long Position { get; set; } = 0;
|
||||||
|
|
||||||
@ -178,7 +179,7 @@ Check the guides for your platform on how to setup ffmpeg correctly:
|
|||||||
public override int Read(byte[] buffer, int offset, int count)
|
public override int Read(byte[] buffer, int offset, int count)
|
||||||
{
|
{
|
||||||
int read = CurrentFileStream.Read(buffer, offset, count);
|
int read = CurrentFileStream.Read(buffer, offset, count);
|
||||||
if(read < count)
|
if (read < count)
|
||||||
{
|
{
|
||||||
if (!BufferingCompleted || IsNextFileReady())
|
if (!BufferingCompleted || IsNextFileReady())
|
||||||
{
|
{
|
||||||
|
212
src/NadekoBot/Modules/Music/Classes/SongHandler.cs
Normal file
212
src/NadekoBot/Modules/Music/Classes/SongHandler.cs
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using VideoLibrary;
|
||||||
|
|
||||||
|
namespace NadekoBot.Modules.Music.Classes
|
||||||
|
{
|
||||||
|
public static class SongHandler
|
||||||
|
{
|
||||||
|
public static async Task<Song> ResolveSong(string query, MusicType musicType = MusicType.Normal)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(query))
|
||||||
|
throw new ArgumentNullException(nameof(query));
|
||||||
|
|
||||||
|
if (musicType != MusicType.Local && IsRadioLink(query))
|
||||||
|
{
|
||||||
|
musicType = MusicType.Radio;
|
||||||
|
query = await HandleStreamContainers(query).ConfigureAwait(false) ?? query;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
switch (musicType)
|
||||||
|
{
|
||||||
|
case MusicType.Local:
|
||||||
|
return new Song(new SongInfo
|
||||||
|
{
|
||||||
|
Uri = "\"" + Path.GetFullPath(query) + "\"",
|
||||||
|
Title = Path.GetFileNameWithoutExtension(query),
|
||||||
|
Provider = "Local File",
|
||||||
|
ProviderType = musicType,
|
||||||
|
Query = query,
|
||||||
|
});
|
||||||
|
case MusicType.Radio:
|
||||||
|
return new Song(new SongInfo
|
||||||
|
{
|
||||||
|
Uri = query,
|
||||||
|
Title = $"{query}",
|
||||||
|
Provider = "Radio Stream",
|
||||||
|
ProviderType = musicType,
|
||||||
|
Query = query
|
||||||
|
})
|
||||||
|
{ TotalTime = TimeSpan.MaxValue };
|
||||||
|
}
|
||||||
|
if (SoundCloud.Default.IsSoundCloudLink(query))
|
||||||
|
{
|
||||||
|
var svideo = await SoundCloud.Default.ResolveVideoAsync(query).ConfigureAwait(false);
|
||||||
|
return new Song(new SongInfo
|
||||||
|
{
|
||||||
|
Title = svideo.FullName,
|
||||||
|
Provider = "SoundCloud",
|
||||||
|
Uri = svideo.StreamLink,
|
||||||
|
ProviderType = musicType,
|
||||||
|
Query = svideo.TrackLink,
|
||||||
|
AlbumArt = svideo.artwork_url,
|
||||||
|
})
|
||||||
|
{ TotalTime = TimeSpan.FromMilliseconds(svideo.Duration) };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (musicType == MusicType.Soundcloud)
|
||||||
|
{
|
||||||
|
var svideo = await SoundCloud.Default.GetVideoByQueryAsync(query).ConfigureAwait(false);
|
||||||
|
return new Song(new SongInfo
|
||||||
|
{
|
||||||
|
Title = svideo.FullName,
|
||||||
|
Provider = "SoundCloud",
|
||||||
|
Uri = svideo.StreamLink,
|
||||||
|
ProviderType = MusicType.Soundcloud,
|
||||||
|
Query = svideo.TrackLink,
|
||||||
|
AlbumArt = svideo.artwork_url,
|
||||||
|
})
|
||||||
|
{ TotalTime = TimeSpan.FromMilliseconds(svideo.Duration) };
|
||||||
|
}
|
||||||
|
|
||||||
|
var link = (await NadekoBot.Google.GetVideosByKeywordsAsync(query).ConfigureAwait(false)).FirstOrDefault();
|
||||||
|
if (string.IsNullOrWhiteSpace(link))
|
||||||
|
throw new OperationCanceledException("Not a valid youtube query.");
|
||||||
|
var allVideos = await Task.Run(async () => { try { return await YouTube.Default.GetAllVideosAsync(link).ConfigureAwait(false); } catch { return Enumerable.Empty<YouTubeVideo>(); } }).ConfigureAwait(false);
|
||||||
|
var videos = allVideos.Where(v => v.AdaptiveKind == AdaptiveKind.Audio);
|
||||||
|
var video = videos
|
||||||
|
.Where(v => v.AudioBitrate < 256)
|
||||||
|
.OrderByDescending(v => v.AudioBitrate)
|
||||||
|
.FirstOrDefault();
|
||||||
|
|
||||||
|
if (video == null) // do something with this error
|
||||||
|
throw new Exception("Could not load any video elements based on the query.");
|
||||||
|
var m = Regex.Match(query, @"\?t=(?<t>\d*)");
|
||||||
|
int gotoTime = 0;
|
||||||
|
if (m.Captures.Count > 0)
|
||||||
|
int.TryParse(m.Groups["t"].ToString(), out gotoTime);
|
||||||
|
var song = new Song(new SongInfo
|
||||||
|
{
|
||||||
|
Title = video.Title.Substring(0, video.Title.Length - 10), // removing trailing "- You Tube"
|
||||||
|
Provider = "YouTube",
|
||||||
|
Uri = video.Uri,
|
||||||
|
Query = link,
|
||||||
|
ProviderType = musicType,
|
||||||
|
});
|
||||||
|
song.SkipTo = gotoTime;
|
||||||
|
return song;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Failed resolving the link.{ex.Message}");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<string> HandleStreamContainers(string query)
|
||||||
|
{
|
||||||
|
string file = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var http = new HttpClient())
|
||||||
|
{
|
||||||
|
file = await http.GetStringAsync(query).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
if (query.Contains(".pls"))
|
||||||
|
{
|
||||||
|
//File1=http://armitunes.com:8000/
|
||||||
|
//Regex.Match(query)
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var m = Regex.Match(file, "File1=(?<url>.*?)\\n");
|
||||||
|
var res = m.Groups["url"]?.ToString();
|
||||||
|
return res?.Trim();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Failed reading .pls:\n{file}");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (query.Contains(".m3u"))
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
# This is a comment
|
||||||
|
C:\xxx4xx\xxxxxx3x\xx2xxxx\xx.mp3
|
||||||
|
C:\xxx5xx\x6xxxxxx\x7xxxxx\xx.mp3
|
||||||
|
*/
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var m = Regex.Match(file, "(?<url>^[^#].*)", RegexOptions.Multiline);
|
||||||
|
var res = m.Groups["url"]?.ToString();
|
||||||
|
return res?.Trim();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Failed reading .m3u:\n{file}");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if (query.Contains(".asx"))
|
||||||
|
{
|
||||||
|
//<ref href="http://armitunes.com:8000"/>
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var m = Regex.Match(file, "<ref href=\"(?<url>.*?)\"");
|
||||||
|
var res = m.Groups["url"]?.ToString();
|
||||||
|
return res?.Trim();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Failed reading .asx:\n{file}");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (query.Contains(".xspf"))
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<playlist version="1" xmlns="http://xspf.org/ns/0/">
|
||||||
|
<trackList>
|
||||||
|
<track><location>file:///mp3s/song_1.mp3</location></track>
|
||||||
|
*/
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var m = Regex.Match(file, "<location>(?<url>.*?)</location>");
|
||||||
|
var res = m.Groups["url"]?.ToString();
|
||||||
|
return res?.Trim();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Failed reading .xspf:\n{file}");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsRadioLink(string query) =>
|
||||||
|
(query.StartsWith("http") ||
|
||||||
|
query.StartsWith("ww"))
|
||||||
|
&&
|
||||||
|
(query.Contains(".pls") ||
|
||||||
|
query.Contains(".m3u") ||
|
||||||
|
query.Contains(".asx") ||
|
||||||
|
query.Contains(".xspf"));
|
||||||
|
}
|
||||||
|
}
|
@ -96,14 +96,17 @@ namespace NadekoBot.Modules.Music
|
|||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
public Task Destroy()
|
public async Task Destroy()
|
||||||
{
|
{
|
||||||
MusicPlayer musicPlayer;
|
var channel = (ITextChannel)umsg.Channel;
|
||||||
if (!MusicPlayers.TryGetValue(Context.Guild.Id, out musicPlayer)) return Task.CompletedTask;
|
await channel.SendErrorAsync("This command is temporarily disabled.").ConfigureAwait(false);
|
||||||
if (((IGuildUser)Context.User).VoiceChannel == musicPlayer.PlaybackVoiceChannel)
|
|
||||||
if (MusicPlayers.TryRemove(Context.Guild.Id, out musicPlayer))
|
/*MusicPlayer musicPlayer;
|
||||||
|
if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer)) return Task.CompletedTask;
|
||||||
|
if (((IGuildUser)umsg.Author).VoiceChannel == musicPlayer.PlaybackVoiceChannel)
|
||||||
|
if(MusicPlayers.TryRemove(channel.Guild.Id, out musicPlayer))
|
||||||
musicPlayer.Destroy();
|
musicPlayer.Destroy();
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;*/
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
@ -118,6 +121,20 @@ namespace NadekoBot.Modules.Music
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
public async Task Fairplay()
|
||||||
|
{
|
||||||
|
var channel = (ITextChannel)Context.Channel;
|
||||||
|
MusicPlayer musicPlayer;
|
||||||
|
if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer)) return;
|
||||||
|
if (((IGuildUser)umsg.Author).VoiceChannel != musicPlayer.PlaybackVoiceChannel)
|
||||||
|
return;
|
||||||
|
var val = musicPlayer.FairPlay = !musicPlayer.FairPlay;
|
||||||
|
|
||||||
|
await channel.SendConfirmAsync("Fair play " + (val ? "enabled" : "disabled") + ".").ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
public async Task Queue([Remainder] string query)
|
public async Task Queue([Remainder] string query)
|
||||||
@ -125,8 +142,7 @@ namespace NadekoBot.Modules.Music
|
|||||||
await QueueSong(((IGuildUser)Context.User), (ITextChannel)Context.Channel, ((IGuildUser)Context.User).VoiceChannel, query).ConfigureAwait(false);
|
await QueueSong(((IGuildUser)Context.User), (ITextChannel)Context.Channel, ((IGuildUser)Context.User).VoiceChannel, query).ConfigureAwait(false);
|
||||||
if ((await Context.Guild.GetCurrentUserAsync()).GetPermissions((IGuildChannel)Context.Channel).ManageMessages)
|
if ((await Context.Guild.GetCurrentUserAsync()).GetPermissions((IGuildChannel)Context.Channel).ManageMessages)
|
||||||
{
|
{
|
||||||
await Task.Delay(10000).ConfigureAwait(false);
|
Context.Message.DeleteAfter(10);
|
||||||
await Context.Message.DeleteAsync().ConfigureAwait(false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,8 +153,7 @@ namespace NadekoBot.Modules.Music
|
|||||||
await QueueSong(((IGuildUser)Context.User), (ITextChannel)Context.Channel, ((IGuildUser)Context.User).VoiceChannel, query, musicType: MusicType.Soundcloud).ConfigureAwait(false);
|
await QueueSong(((IGuildUser)Context.User), (ITextChannel)Context.Channel, ((IGuildUser)Context.User).VoiceChannel, query, musicType: MusicType.Soundcloud).ConfigureAwait(false);
|
||||||
if ((await Context.Guild.GetCurrentUserAsync()).GetPermissions((IGuildChannel)Context.Channel).ManageMessages)
|
if ((await Context.Guild.GetCurrentUserAsync()).GetPermissions((IGuildChannel)Context.Channel).ManageMessages)
|
||||||
{
|
{
|
||||||
await Task.Delay(10000).ConfigureAwait(false);
|
umsg.DeleteAfter(10);
|
||||||
await Context.Message.DeleteAsync().ConfigureAwait(false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,28 +173,44 @@ namespace NadekoBot.Modules.Music
|
|||||||
|
|
||||||
var currentSong = musicPlayer.CurrentSong;
|
var currentSong = musicPlayer.CurrentSong;
|
||||||
if (currentSong == null)
|
if (currentSong == null)
|
||||||
return;
|
|
||||||
|
|
||||||
if (currentSong.TotalLength == TimeSpan.Zero)
|
|
||||||
{
|
{
|
||||||
await musicPlayer.UpdateSongDurationsAsync().ConfigureAwait(false);
|
await channel.SendErrorAsync("🎵 No active music player.").ConfigureAwait(false);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//var toSend = $"🎵 Currently Playing {currentSong.PrettyName} " + $"`{currentSong.PrettyCurrentTime()}`\n";
|
try { await musicPlayer.UpdateSongDurationsAsync().ConfigureAwait(false); } catch { }
|
||||||
var toSend = $"🎵 Currently Playing {currentSong.PrettyName}\n";
|
|
||||||
if (musicPlayer.RepeatSong)
|
const int itemsPerPage = 10;
|
||||||
toSend += "🔂";
|
|
||||||
else if (musicPlayer.RepeatPlaylist)
|
|
||||||
toSend += "🔁";
|
|
||||||
toSend += $" `{musicPlayer.Playlist.Count} tracks currently queued. Showing page {page}:` ";
|
|
||||||
if (musicPlayer.MaxQueueSize != 0 && musicPlayer.Playlist.Count >= musicPlayer.MaxQueueSize)
|
|
||||||
toSend += "**Song queue is full!**\n";
|
|
||||||
else
|
|
||||||
toSend += "\n";
|
|
||||||
const int itemsPerPage = 15;
|
|
||||||
int startAt = itemsPerPage * (page - 1);
|
int startAt = itemsPerPage * (page - 1);
|
||||||
var number = 1 + startAt;
|
var number = 0 + startAt;
|
||||||
await Context.Channel.SendConfirmAsync(toSend + string.Join("\n", musicPlayer.Playlist.Skip(startAt).Take(15).Select(v => $"`{number++}.` {v.PrettyName}"))).ConfigureAwait(false);
|
|
||||||
|
var total = musicPlayer.TotalPlaytime;
|
||||||
|
var maxPlaytime = musicPlayer.MaxPlaytimeSeconds;
|
||||||
|
var embed = new EmbedBuilder()
|
||||||
|
.WithAuthor(eab => eab.WithName($"Player Queue - Page {page}")
|
||||||
|
.WithMusicIcon())
|
||||||
|
.WithDescription(string.Join("\n", musicPlayer.Playlist
|
||||||
|
.Skip(startAt)
|
||||||
|
.Take(10)
|
||||||
|
.Select(v => $"`{++number}.` {v.PrettyFullName}")))
|
||||||
|
.WithFooter(ef => ef.WithText($"{musicPlayer.PrettyVolume} | {musicPlayer.Playlist.Count} " +
|
||||||
|
$"{("tracks".SnPl(musicPlayer.Playlist.Count))} | {(int)total.TotalHours}h {total.Minutes}m {total.Seconds}s | " +
|
||||||
|
(musicPlayer.FairPlay? "✔️fairplay" : "✖️fairplay") + $" | " + (maxPlaytime == 0 ? "unlimited" : $"{maxPlaytime}s limit")))
|
||||||
|
.WithOkColor();
|
||||||
|
|
||||||
|
if (musicPlayer.RepeatSong)
|
||||||
|
{
|
||||||
|
embed.WithTitle($"🔂 Repeating Song: {currentSong.SongInfo.Title} | {currentSong.PrettyFullTime}");
|
||||||
|
}
|
||||||
|
else if (musicPlayer.RepeatPlaylist)
|
||||||
|
{
|
||||||
|
embed.WithTitle("🔁 Repeating Playlist");
|
||||||
|
}
|
||||||
|
if (musicPlayer.MaxQueueSize != 0 && musicPlayer.Playlist.Count >= musicPlayer.MaxQueueSize)
|
||||||
|
{
|
||||||
|
embed.WithTitle("🎵 Song queue is full!");
|
||||||
|
}
|
||||||
|
await channel.EmbedAsync(embed.Build()).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
@ -192,28 +223,15 @@ namespace NadekoBot.Modules.Music
|
|||||||
var currentSong = musicPlayer.CurrentSong;
|
var currentSong = musicPlayer.CurrentSong;
|
||||||
if (currentSong == null)
|
if (currentSong == null)
|
||||||
return;
|
return;
|
||||||
var videoid = Regex.Match(currentSong.SongInfo.Query, "<=v=[a-zA-Z0-9-]+(?=&)|(?<=[0-9])[^&\n]+|(?<=v=)[^&\n]+");
|
try { await musicPlayer.UpdateSongDurationsAsync().ConfigureAwait(false); } catch { }
|
||||||
|
|
||||||
if (currentSong.TotalLength == TimeSpan.Zero)
|
var embed = new EmbedBuilder().WithOkColor()
|
||||||
{
|
.WithAuthor(eab => eab.WithName("Now Playing").WithMusicIcon())
|
||||||
await musicPlayer.UpdateSongDurationsAsync().ConfigureAwait(false);
|
.WithDescription(currentSong.PrettyName)
|
||||||
}
|
.WithThumbnail(tn => tn.Url = currentSong.Thumbnail)
|
||||||
var embed = new EmbedBuilder()
|
.WithFooter(ef => ef.WithText(musicPlayer.PrettyVolume + " | " + currentSong.PrettyFullTime + $" | {currentSong.PrettyProvider} | {currentSong.QueuerName}"));
|
||||||
.WithAuthor(eab => eab.WithName("Now Playing").WithIconUrl("https://cdn.discordapp.com/attachments/155726317222887425/258605269972549642/music1.png"))
|
|
||||||
.WithTitle($"{currentSong.SongInfo.Title}")
|
await channel.EmbedAsync(embed).ConfigureAwait(false);
|
||||||
.WithUrl($"{currentSong.SongInfo.Query}")
|
|
||||||
.WithDescription($"{currentSong.PrettyCurrentTime()}")
|
|
||||||
.WithFooter(ef => ef.WithText($"{currentSong.PrettyProvider} | {currentSong.PrettyUser}"))
|
|
||||||
.WithColor(NadekoBot.OkColor);
|
|
||||||
if (currentSong.SongInfo.Provider.Equals("YouTube", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
embed.WithThumbnailUrl($"https://img.youtube.com/vi/{videoid}/0.jpg");
|
|
||||||
}
|
|
||||||
else if (currentSong.SongInfo.Provider.Equals("SoundCloud", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
embed.WithThumbnailUrl($"{currentSong.SongInfo.AlbumArt}");
|
|
||||||
}
|
|
||||||
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
@ -295,25 +313,31 @@ namespace NadekoBot.Modules.Music
|
|||||||
await Context.Channel.SendErrorAsync($"🎵 Failed to find any songs.").ConfigureAwait(false);
|
await Context.Channel.SendErrorAsync($"🎵 Failed to find any songs.").ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var idArray = ids as string[] ?? ids.ToArray();
|
var count = ids.Count();
|
||||||
var count = idArray.Length;
|
|
||||||
var msg =
|
var msg = await channel.SendMessageAsync($"🎵 Attempting to queue **{count}** songs".SnPl(count) + "...").ConfigureAwait(false);
|
||||||
await Context.Channel.SendMessageAsync($"🎵 Attempting to queue **{count}** songs".SnPl(count) + "...").ConfigureAwait(false);
|
|
||||||
var cancelSource = new CancellationTokenSource();
|
var cancelSource = new CancellationTokenSource();
|
||||||
|
|
||||||
var tasks = Task.WhenAll(idArray.Select(async id =>
|
var gusr = (IGuildUser)umsg.Author;
|
||||||
{
|
|
||||||
if (cancelSource.Token.IsCancellationRequested)
|
|
||||||
return;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await QueueSong(((IGuildUser)Context.User), (ITextChannel)Context.Channel, ((IGuildUser)Context.User).VoiceChannel, id, true).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
catch (SongNotFoundException) { }
|
|
||||||
catch { try { cancelSource.Cancel(); } catch { } }
|
|
||||||
}));
|
|
||||||
|
|
||||||
await Task.WhenAny(tasks, Task.Delay(Timeout.Infinite, cancelSource.Token));
|
while (ids.Any() && !cancelSource.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
var tasks = Task.WhenAll(ids.Take(5).Select(async id =>
|
||||||
|
{
|
||||||
|
if (cancelSource.Token.IsCancellationRequested)
|
||||||
|
return;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await QueueSong(gusr, channel, gusr.VoiceChannel, id, true).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch (SongNotFoundException) { }
|
||||||
|
catch { try { cancelSource.Cancel(); } catch { } }
|
||||||
|
}));
|
||||||
|
|
||||||
|
await Task.WhenAny(tasks, Task.Delay(Timeout.Infinite, cancelSource.Token));
|
||||||
|
ids = ids.Skip(5);
|
||||||
|
}
|
||||||
|
|
||||||
await msg.ModifyAsync(m => m.Content = "✅ Playlist queue complete.").ConfigureAwait(false);
|
await msg.ModifyAsync(m => m.Content = "✅ Playlist queue complete.").ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
@ -369,6 +393,7 @@ namespace NadekoBot.Modules.Music
|
|||||||
var dir = new DirectoryInfo(arg);
|
var dir = new DirectoryInfo(arg);
|
||||||
var fileEnum = dir.GetFiles("*", SearchOption.AllDirectories)
|
var fileEnum = dir.GetFiles("*", SearchOption.AllDirectories)
|
||||||
.Where(x => !x.Attributes.HasFlag(FileAttributes.Hidden | FileAttributes.System));
|
.Where(x => !x.Attributes.HasFlag(FileAttributes.Hidden | FileAttributes.System));
|
||||||
|
var gusr = (IGuildUser)umsg.Author;
|
||||||
foreach (var file in fileEnum)
|
foreach (var file in fileEnum)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@ -399,8 +424,7 @@ namespace NadekoBot.Modules.Music
|
|||||||
await QueueSong(((IGuildUser)Context.User), (ITextChannel)Context.Channel, ((IGuildUser)Context.User).VoiceChannel, radio_link, musicType: MusicType.Radio).ConfigureAwait(false);
|
await QueueSong(((IGuildUser)Context.User), (ITextChannel)Context.Channel, ((IGuildUser)Context.User).VoiceChannel, radio_link, musicType: MusicType.Radio).ConfigureAwait(false);
|
||||||
if ((await Context.Guild.GetCurrentUserAsync()).GetPermissions((IGuildChannel)Context.Channel).ManageMessages)
|
if ((await Context.Guild.GetCurrentUserAsync()).GetPermissions((IGuildChannel)Context.Channel).ManageMessages)
|
||||||
{
|
{
|
||||||
await Task.Delay(10000).ConfigureAwait(false);
|
Context.Message.DeleteAfter(10);
|
||||||
await Context.Message.DeleteAsync().ConfigureAwait(false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -447,7 +471,15 @@ namespace NadekoBot.Modules.Music
|
|||||||
return;
|
return;
|
||||||
var song = (musicPlayer.Playlist as List<Song>)?[num - 1];
|
var song = (musicPlayer.Playlist as List<Song>)?[num - 1];
|
||||||
musicPlayer.RemoveSongAt(num - 1);
|
musicPlayer.RemoveSongAt(num - 1);
|
||||||
await Context.Channel.SendConfirmAsync($"🎵 Track {song.PrettyName} at position `#{num}` has been **removed**.").ConfigureAwait(false);
|
|
||||||
|
var embed = new EmbedBuilder()
|
||||||
|
.WithAuthor(eab => eab.WithName("Song Removed!").WithMusicIcon())
|
||||||
|
.AddField(fb => fb.WithName("**Song Position**").WithValue($"#{num}").WithIsInline(true))
|
||||||
|
.AddField(fb => fb.WithName("**Song Name**").WithValue(song.PrettyName).WithIsInline(true))
|
||||||
|
.WithFooter(ef => ef.WithText($"{song.PrettyProvider} | {song.QueuerName}"))
|
||||||
|
.WithErrorColor();
|
||||||
|
|
||||||
|
await channel.EmbedAsync(embed).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
@ -497,14 +529,14 @@ namespace NadekoBot.Modules.Music
|
|||||||
var nn1 = n2 < n1 ? n1 : n1 - 1;
|
var nn1 = n2 < n1 ? n1 : n1 - 1;
|
||||||
playlist.RemoveAt(nn1);
|
playlist.RemoveAt(nn1);
|
||||||
|
|
||||||
var embed = new EmbedBuilder()
|
var embed = new EmbedBuilder()
|
||||||
.WithTitle($"{s.SongInfo.Title.TrimTo(70)}")
|
.WithTitle($"{s.SongInfo.Title.TrimTo(70)}")
|
||||||
.WithUrl($"{s.SongInfo.Query}")
|
.WithUrl($"{s.SongInfo.Query}")
|
||||||
.WithAuthor(eab => eab.WithName("Song Moved").WithIconUrl("https://cdn.discordapp.com/attachments/155726317222887425/258605269972549642/music1.png"))
|
.WithAuthor(eab => eab.WithName("Song Moved").WithIconUrl("https://cdn.discordapp.com/attachments/155726317222887425/258605269972549642/music1.png"))
|
||||||
.AddField(fb => fb.WithName("**From**").WithValue($"#{n1}").WithIsInline(true))
|
.AddField(fb => fb.WithName("**From Position**").WithValue($"#{n1}").WithIsInline(true))
|
||||||
.AddField(fb => fb.WithName("**To**").WithValue($"#{n2}").WithIsInline(true))
|
.AddField(fb => fb.WithName("**To Position**").WithValue($"#{n2}").WithIsInline(true))
|
||||||
.WithColor(NadekoBot.OkColor);
|
.WithColor(NadekoBot.OkColor);
|
||||||
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
|
await channel.EmbedAsync(embed.Build()).ConfigureAwait(false);
|
||||||
|
|
||||||
//await channel.SendConfirmAsync($"🎵Moved {s.PrettyName} `from #{n1} to #{n2}`").ConfigureAwait(false);
|
//await channel.SendConfirmAsync($"🎵Moved {s.PrettyName} `from #{n1} to #{n2}`").ConfigureAwait(false);
|
||||||
|
|
||||||
@ -519,14 +551,31 @@ namespace NadekoBot.Modules.Music
|
|||||||
if (!MusicPlayers.TryGetValue(Context.Guild.Id, out musicPlayer))
|
if (!MusicPlayers.TryGetValue(Context.Guild.Id, out musicPlayer))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
musicPlayer.MaxQueueSize = size;
|
musicPlayer.MaxQueueSize = size;
|
||||||
await Context.Channel.SendConfirmAsync($"🎵 Max queue set to {(size == 0 ? ("unlimited") : size + " tracks")}.");
|
await Context.Channel.SendConfirmAsync($"🎵 Max queue set to {(size == 0 ? ("unlimited") : size + " tracks")}.");
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
public async Task ReptCurSong()
|
public async Task SetMaxPlaytime(uint seconds)
|
||||||
|
{
|
||||||
|
if (seconds < 15 && seconds != 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var channel = (ITextChannel)Context.Channel;
|
||||||
|
MusicPlayer musicPlayer;
|
||||||
|
if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer))
|
||||||
|
return;
|
||||||
|
musicPlayer.MaxPlaytimeSeconds = seconds;
|
||||||
|
if(seconds == 0)
|
||||||
|
await channel.SendConfirmAsync($"🎵 Max playtime has no limit now.");
|
||||||
|
else
|
||||||
|
await channel.SendConfirmAsync($"🎵 Max playtime set to {seconds} seconds.");
|
||||||
|
}
|
||||||
|
|
||||||
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
public async Task ReptCurSong(IUserMessage umsg)
|
||||||
{
|
{
|
||||||
|
|
||||||
MusicPlayer musicPlayer;
|
MusicPlayer musicPlayer;
|
||||||
@ -536,9 +585,15 @@ namespace NadekoBot.Modules.Music
|
|||||||
if (currentSong == null)
|
if (currentSong == null)
|
||||||
return;
|
return;
|
||||||
var currentValue = musicPlayer.ToggleRepeatSong();
|
var currentValue = musicPlayer.ToggleRepeatSong();
|
||||||
await Context.Channel.SendConfirmAsync(currentValue ?
|
|
||||||
$"🔂 Repeating track: {currentSong.PrettyName}" :
|
if (currentValue)
|
||||||
$"🔂 Current track repeat stopped.")
|
await channel.EmbedAsync(new EmbedBuilder()
|
||||||
|
.WithOkColor()
|
||||||
|
.WithAuthor(eab => eab.WithMusicIcon().WithName("🔂 Repeating track"))
|
||||||
|
.WithDescription(currentSong.PrettyName)
|
||||||
|
.WithFooter(ef => ef.WithText(currentSong.PrettyInfo))).ConfigureAwait(false);
|
||||||
|
else
|
||||||
|
await Context.Channel.SendConfirmAsync($"🔂 Current track repeat stopped.")
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -638,9 +693,12 @@ namespace NadekoBot.Modules.Music
|
|||||||
playlists = uow.MusicPlaylists.GetPlaylistsOnPage(num);
|
playlists = uow.MusicPlaylists.GetPlaylistsOnPage(num);
|
||||||
}
|
}
|
||||||
|
|
||||||
await Context.Channel.SendConfirmAsync($@"🎶 **Page {num} of saved playlists:**
|
var embed = new EmbedBuilder()
|
||||||
|
.WithAuthor(eab => eab.WithName($"Page {num} of Saved Playlists").WithMusicIcon())
|
||||||
|
.WithDescription(string.Join("\n", playlists.Select(r => $"`#{r.Id}` - **{r.Name}** by *{r.Author}* ({r.Songs.Count} songs)")))
|
||||||
|
.WithOkColor();
|
||||||
|
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
|
||||||
|
|
||||||
" + string.Join("\n", playlists.Select(r => $"`#{r.Id}` - **{r.Name}** by __{r.Author}__ ({r.Songs.Count} songs)"))).ConfigureAwait(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//todo only author or owner
|
//todo only author or owner
|
||||||
@ -719,42 +777,7 @@ namespace NadekoBot.Modules.Music
|
|||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
public async Task GetLink(int index = 0)
|
public async Task Autoplay(IUserMessage umsg)
|
||||||
{
|
|
||||||
|
|
||||||
MusicPlayer musicPlayer;
|
|
||||||
if (!MusicPlayers.TryGetValue(Context.Guild.Id, out musicPlayer))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (index < 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (index > 0)
|
|
||||||
{
|
|
||||||
|
|
||||||
var selSong = musicPlayer.Playlist.DefaultIfEmpty(null).ElementAtOrDefault(index - 1);
|
|
||||||
if (selSong == null)
|
|
||||||
{
|
|
||||||
await Context.Channel.SendErrorAsync("Could not select song, likely wrong index");
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await Context.Channel.SendConfirmAsync($"🎶 Selected song **{selSong.SongInfo.Title}**: <{selSong.SongInfo.Query}>").ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var curSong = musicPlayer.CurrentSong;
|
|
||||||
if (curSong == null)
|
|
||||||
return;
|
|
||||||
await Context.Channel.SendConfirmAsync($"🎶 Current song **{curSong.SongInfo.Title}**: <{curSong.SongInfo.Query}>").ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
|
||||||
[RequireContext(ContextType.Guild)]
|
|
||||||
public async Task Autoplay()
|
|
||||||
{
|
{
|
||||||
MusicPlayer musicPlayer;
|
MusicPlayer musicPlayer;
|
||||||
if (!MusicPlayers.TryGetValue(Context.Guild.Id, out musicPlayer))
|
if (!MusicPlayers.TryGetValue(Context.Guild.Id, out musicPlayer))
|
||||||
@ -786,56 +809,67 @@ namespace NadekoBot.Modules.Music
|
|||||||
}
|
}
|
||||||
var mp = new MusicPlayer(voiceCh, vol);
|
var mp = new MusicPlayer(voiceCh, vol);
|
||||||
IUserMessage playingMessage = null;
|
IUserMessage playingMessage = null;
|
||||||
IUserMessage lastFinishedMessage = null;
|
IUserMessage lastFinishedMessage = null;
|
||||||
mp.OnCompleted += async (s, song) =>
|
mp.OnCompleted += async (s, song) =>
|
||||||
{
|
{
|
||||||
if (song.PrintStatusMessage)
|
try
|
||||||
{
|
{
|
||||||
try
|
if (finishedMessage != null)
|
||||||
{
|
finishedMessage.DeleteAfter(0);
|
||||||
if (lastFinishedMessage != null)
|
|
||||||
await lastFinishedMessage.DeleteAsync().ConfigureAwait(false);
|
|
||||||
if (playingMessage != null)
|
|
||||||
await playingMessage.DeleteAsync().ConfigureAwait(false);
|
|
||||||
try { lastFinishedMessage = await textCh.SendConfirmAsync($"🎵 Finished {song.PrettyName}").ConfigureAwait(false); } catch { }
|
|
||||||
if (mp.Autoplay && mp.Playlist.Count == 0 && song.SongInfo.Provider == "YouTube")
|
|
||||||
{
|
|
||||||
await QueueSong(await queuer.Guild.GetCurrentUserAsync(), textCh, voiceCh, (await NadekoBot.Google.GetRelatedVideosAsync(song.SongInfo.Query, 4)).ToList().Shuffle().FirstOrDefault(), silent, musicType).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch { }
|
|
||||||
}
|
|
||||||
};
|
|
||||||
mp.OnStarted += async (s, song) =>
|
|
||||||
{
|
|
||||||
if (song.PrintStatusMessage)
|
|
||||||
{
|
|
||||||
var sender = s as MusicPlayer;
|
|
||||||
if (sender == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var msgTxt = $"🎵 Playing {song.PrettyName}\t `Vol: {(int)(sender.Volume * 100)}%`";
|
finishedMessage = await textCh.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||||
try { playingMessage = await textCh.SendConfirmAsync(msgTxt).ConfigureAwait(false); } catch { }
|
.WithAuthor(eab => eab.WithName("Finished Song").WithMusicIcon())
|
||||||
}
|
.WithDescription(song.PrettyName)
|
||||||
};
|
.WithFooter(ef => ef.WithText(song.PrettyInfo))
|
||||||
mp.OnPauseChanged += async (paused) =>
|
.Build())
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
|
if (mp.Autoplay && mp.Playlist.Count == 0 && song.SongInfo.Provider == "YouTube")
|
||||||
{
|
{
|
||||||
try
|
await QueueSong(queuer.Guild.GetCurrentUser(), textCh, voiceCh, (await NadekoBot.Google.GetRelatedVideosAsync(song.SongInfo.Query, 4)).ToList().Shuffle().FirstOrDefault(), silent, musicType).ConfigureAwait(false);
|
||||||
{
|
}
|
||||||
if (paused)
|
}
|
||||||
await textCh.SendConfirmAsync("🎵 Music playback **paused**.").ConfigureAwait(false);
|
catch { }
|
||||||
else
|
};
|
||||||
await textCh.SendConfirmAsync("🎵 Music playback **resumed**.").ConfigureAwait(false);
|
IUserMessage playingMessage = null;
|
||||||
}
|
|
||||||
catch { }
|
mp.OnStarted += async (player, song) =>
|
||||||
};
|
{
|
||||||
|
try { await mp.UpdateSongDurationsAsync().ConfigureAwait(false); } catch { }
|
||||||
|
var sender = player as MusicPlayer;
|
||||||
|
if (sender == null)
|
||||||
|
return;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (playingMessage != null)
|
||||||
|
playingMessage.DeleteAfter(0);
|
||||||
|
|
||||||
|
playingMessage = await textCh.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||||
|
.WithAuthor(eab => eab.WithName("Playing Song").WithMusicIcon())
|
||||||
|
.WithDescription(song.PrettyName)
|
||||||
|
.WithFooter(ef => ef.WithText(song.PrettyInfo)))
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
};
|
||||||
|
mp.OnPauseChanged += async (paused) =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (paused)
|
||||||
|
await textCh.SendConfirmAsync("🎵 Music playback **paused**.").ConfigureAwait(false);
|
||||||
|
else
|
||||||
|
await textCh.SendConfirmAsync("🎵 Music playback **resumed**.").ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
};
|
||||||
return mp;
|
return mp;
|
||||||
});
|
});
|
||||||
Song resolvedSong;
|
Song resolvedSong;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
musicPlayer.ThrowIfQueueFull();
|
musicPlayer.ThrowIfQueueFull();
|
||||||
resolvedSong = await Song.ResolveSong(query, musicType).ConfigureAwait(false);
|
resolvedSong = await SongHandler.ResolveSong(query, musicType).ConfigureAwait(false);
|
||||||
|
|
||||||
if (resolvedSong == null)
|
if (resolvedSong == null)
|
||||||
throw new SongNotFoundException();
|
throw new SongNotFoundException();
|
||||||
@ -844,24 +878,22 @@ namespace NadekoBot.Modules.Music
|
|||||||
}
|
}
|
||||||
catch (PlaylistFullException)
|
catch (PlaylistFullException)
|
||||||
{
|
{
|
||||||
try { await textCh.SendConfirmAsync($"🎵 Queue is full at **{musicPlayer.MaxQueueSize}/{musicPlayer.MaxQueueSize}**. "); } catch { }
|
try { await textCh.SendConfirmAsync($"🎵 Queue is full at **{musicPlayer.MaxQueueSize}/{musicPlayer.MaxQueueSize}**."); } catch { }
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
if (!silent)
|
if (!silent)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var queuedMessage = await textCh.SendConfirmAsync($"🎵 Queued **{resolvedSong.SongInfo.Title.TrimTo(55)}** at `#{musicPlayer.Playlist.Count + 1}`").ConfigureAwait(false);
|
//var queuedMessage = await textCh.SendConfirmAsync($"🎵 Queued **{resolvedSong.SongInfo.Title}** at `#{musicPlayer.Playlist.Count + 1}`").ConfigureAwait(false);
|
||||||
var t = Task.Run(async () =>
|
var queuedMessage = await textCh.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||||
{
|
.WithAuthor(eab => eab.WithName("Queued Song #" + (musicPlayer.Playlist.Count + 1)).WithMusicIcon())
|
||||||
try
|
.WithDescription($"{resolvedSong.PrettyName}\nQueue ")
|
||||||
{
|
.WithThumbnail(tn => tn.Url = resolvedSong.Thumbnail)
|
||||||
await Task.Delay(10000).ConfigureAwait(false);
|
.WithFooter(ef => ef.WithText(resolvedSong.PrettyProvider)))
|
||||||
|
.ConfigureAwait(false);
|
||||||
await queuedMessage.DeleteAsync().ConfigureAwait(false);
|
if (queuedMessage != null)
|
||||||
}
|
queuedMessage.DeleteAfter(10);
|
||||||
catch { }
|
|
||||||
}).ConfigureAwait(false);
|
|
||||||
}
|
}
|
||||||
catch { } // if queued message sending fails, don't attempt to delete it
|
catch { } // if queued message sending fails, don't attempt to delete it
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using Discord.Commands;
|
using Discord;
|
||||||
|
using Discord.Commands;
|
||||||
using NadekoBot.Attributes;
|
using NadekoBot.Attributes;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using System;
|
using System;
|
||||||
@ -9,26 +10,35 @@ using System.Net.Http;
|
|||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Xml.Linq;
|
using System.Xml.Linq;
|
||||||
using NadekoBot.Extensions;
|
using NadekoBot.Extensions;
|
||||||
|
using System.Xml;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
|
||||||
namespace NadekoBot.Modules.NSFW
|
namespace NadekoBot.Modules.NSFW
|
||||||
{
|
{
|
||||||
[NadekoModule("NSFW", "~")]
|
[NadekoModule("NSFW", "~")]
|
||||||
public class NSFW : DiscordModule
|
public class NSFW : DiscordModule
|
||||||
{
|
{
|
||||||
|
//ulong/cancel
|
||||||
|
private static ConcurrentDictionary<ulong, Timer> AutoHentaiTimers { get; } = new ConcurrentDictionary<ulong, Timer>();
|
||||||
|
|
||||||
public NSFW() : base()
|
public NSFW() : base()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
public async Task Hentai([Remainder] string tag = null)
|
[RequireContext(ContextType.Guild)]
|
||||||
|
public async Task Hentai(IUserMessage umsg, [Remainder] string tag = null)
|
||||||
{
|
{
|
||||||
|
var channel = (ITextChannel)umsg.Channel;
|
||||||
|
|
||||||
tag = tag?.Trim() ?? "";
|
tag = tag?.Trim() ?? "";
|
||||||
|
|
||||||
tag = "rating%3Aexplicit+" + tag;
|
tag = "rating%3Aexplicit+" + tag;
|
||||||
|
|
||||||
var rng = new NadekoRandom();
|
var rng = new NadekoRandom();
|
||||||
Task<string> provider = Task.FromResult("");
|
Task<string> provider = Task.FromResult("");
|
||||||
switch (rng.Next(0,4))
|
switch (rng.Next(0, 4))
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
provider = GetDanbooruImageLink(tag);
|
provider = GetDanbooruImageLink(tag);
|
||||||
@ -47,14 +57,63 @@ namespace NadekoBot.Modules.NSFW
|
|||||||
}
|
}
|
||||||
var link = await provider.ConfigureAwait(false);
|
var link = await provider.ConfigureAwait(false);
|
||||||
if (string.IsNullOrWhiteSpace(link))
|
if (string.IsNullOrWhiteSpace(link))
|
||||||
await Context.Channel.SendErrorAsync("No results found.").ConfigureAwait(false);
|
await channel.SendErrorAsync("No results found.").ConfigureAwait(false);
|
||||||
else
|
else
|
||||||
await Context.Channel.SendMessageAsync(link).ConfigureAwait(false);
|
await channel.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||||
|
.WithImageUrl(link)
|
||||||
|
.WithDescription("Tag: " + tag)
|
||||||
|
.Build()).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
public async Task HentaiBomb([Remainder] string tag = null)
|
[RequireContext(ContextType.Guild)]
|
||||||
|
public async Task AutoHentai(IUserMessage umsg, int interval = 0, string tags = null)
|
||||||
{
|
{
|
||||||
|
Timer t;
|
||||||
|
|
||||||
|
if (interval == 0)
|
||||||
|
{
|
||||||
|
if (AutoHentaiTimers.TryRemove(umsg.Channel.Id, out t))
|
||||||
|
{
|
||||||
|
t.Change(Timeout.Infinite, Timeout.Infinite); //proper way to disable the timer
|
||||||
|
await umsg.Channel.SendConfirmAsync("Autohentai stopped.").ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (interval < 20)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var tagsArr = tags?.Split('|');
|
||||||
|
|
||||||
|
t = new Timer(async (state) =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (tagsArr == null || tagsArr.Length == 0)
|
||||||
|
await Hentai(umsg, null).ConfigureAwait(false);
|
||||||
|
else
|
||||||
|
await Hentai(umsg, tagsArr[new NadekoRandom().Next(0, tagsArr.Length)]);
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}, null, interval * 1000, interval * 1000);
|
||||||
|
|
||||||
|
AutoHentaiTimers.AddOrUpdate(umsg.Channel.Id, t, (key, old) =>
|
||||||
|
{
|
||||||
|
old.Change(Timeout.Infinite, Timeout.Infinite);
|
||||||
|
return t;
|
||||||
|
});
|
||||||
|
|
||||||
|
await umsg.Channel.SendConfirmAsync($"Autohentai started. Reposting every {interval}s with one of the following tags:\n{string.Join(", ", tagsArr)}").ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
public async Task HentaiBomb(IUserMessage umsg, [Remainder] string tag = null)
|
||||||
|
{
|
||||||
|
var channel = (ITextChannel)umsg.Channel;
|
||||||
|
|
||||||
tag = tag?.Trim() ?? "";
|
tag = tag?.Trim() ?? "";
|
||||||
tag = "rating%3Aexplicit+" + tag;
|
tag = "rating%3Aexplicit+" + tag;
|
||||||
|
|
||||||
@ -65,107 +124,88 @@ namespace NadekoBot.Modules.NSFW
|
|||||||
|
|
||||||
if (links.All(l => l == null))
|
if (links.All(l => l == null))
|
||||||
{
|
{
|
||||||
await Context.Channel.SendErrorAsync("No results found.").ConfigureAwait(false);
|
await channel.SendErrorAsync("No results found.").ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await Context.Channel.SendMessageAsync(String.Join("\n\n", links)).ConfigureAwait(false);
|
await channel.SendMessageAsync(String.Join("\n\n", links)).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<string> GetYandereImageLink(string tag)
|
|
||||||
{
|
|
||||||
var rng = new NadekoRandom();
|
|
||||||
var url =
|
|
||||||
$"https://yande.re/post.xml?" +
|
|
||||||
$"limit=25" +
|
|
||||||
$"&page={rng.Next(0, 15)}" +
|
|
||||||
$"&tags={tag.Replace(" ", "_")}";
|
|
||||||
using (var http = new HttpClient())
|
|
||||||
{
|
|
||||||
var webpage = await http.GetStringAsync(url).ConfigureAwait(false);
|
|
||||||
var matches = Regex.Matches(webpage, "file_url=\"(?<url>.*?)\"");
|
|
||||||
//var rating = Regex.Matches(webpage, "rating=\"(?<rate>.*?)\"");
|
|
||||||
if (matches.Count == 0)
|
|
||||||
return null;
|
|
||||||
return matches[rng.Next(0, matches.Count)].Groups["url"].Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
public async Task Yandere([Remainder] string tag = null)
|
[RequireContext(ContextType.Guild)]
|
||||||
|
public async Task Danbooru(IUserMessage umsg, [Remainder] string tag = null)
|
||||||
{
|
{
|
||||||
|
var channel = (ITextChannel)umsg.Channel;
|
||||||
|
|
||||||
tag = tag?.Trim() ?? "";
|
tag = tag?.Trim() ?? "";
|
||||||
var link = await GetYandereImageLink(tag).ConfigureAwait(false);
|
|
||||||
if (string.IsNullOrWhiteSpace(link))
|
var url = await GetDanbooruImageLink(tag).ConfigureAwait(false);
|
||||||
await Context.Channel.SendErrorAsync("No results found.").ConfigureAwait(false);
|
|
||||||
|
if (url == null)
|
||||||
|
await channel.SendErrorAsync(umsg.Author.Mention + " No results.");
|
||||||
else
|
else
|
||||||
await Context.Channel.SendMessageAsync(link).ConfigureAwait(false);
|
await channel.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||||
|
.WithDescription(umsg.Author.Mention + " " + tag)
|
||||||
|
.WithImageUrl(url)
|
||||||
|
.WithFooter(efb => efb.WithText("Danbooru"))
|
||||||
|
.Build()).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
public async Task Danbooru([Remainder] string tag = null)
|
[RequireContext(ContextType.Guild)]
|
||||||
|
public Task Yandere(IUserMessage umsg, [Remainder] string tag = null)
|
||||||
|
=> Searches.Searches.InternalDapiCommand(umsg, tag, Searches.Searches.DapiSearchType.Yandere);
|
||||||
|
|
||||||
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
public Task Konachan(IUserMessage umsg, [Remainder] string tag = null)
|
||||||
|
=> Searches.Searches.InternalDapiCommand(umsg, tag, Searches.Searches.DapiSearchType.Konachan);
|
||||||
|
|
||||||
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
public Task Gelbooru(IUserMessage umsg, [Remainder] string tag = null)
|
||||||
|
=> Searches.Searches.InternalDapiCommand(umsg, tag, Searches.Searches.DapiSearchType.Gelbooru);
|
||||||
|
|
||||||
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
public Task Rule34(IUserMessage umsg, [Remainder] string tag = null)
|
||||||
|
=> Searches.Searches.InternalDapiCommand(umsg, tag, Searches.Searches.DapiSearchType.Rule34);
|
||||||
|
|
||||||
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
public async Task E621(IUserMessage umsg, [Remainder] string tag = null)
|
||||||
{
|
{
|
||||||
|
var channel = (ITextChannel)umsg.Channel;
|
||||||
|
|
||||||
tag = tag?.Trim() ?? "";
|
tag = tag?.Trim() ?? "";
|
||||||
var link = await GetDanbooruImageLink(tag).ConfigureAwait(false);
|
|
||||||
if (string.IsNullOrWhiteSpace(link))
|
var url = await GetE621ImageLink(tag).ConfigureAwait(false);
|
||||||
await Context.Channel.SendErrorAsync("No results found.").ConfigureAwait(false);
|
|
||||||
|
if (url == null)
|
||||||
|
await channel.SendErrorAsync(umsg.Author.Mention + " No results.");
|
||||||
else
|
else
|
||||||
await Context.Channel.SendMessageAsync(link).ConfigureAwait(false);
|
await channel.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||||
|
.WithDescription(umsg.Author.Mention + " " + tag)
|
||||||
|
.WithImageUrl(url)
|
||||||
|
.WithFooter(efb => efb.WithText("e621"))
|
||||||
|
.Build()).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
public async Task Konachan([Remainder] string tag = null)
|
[RequireContext(ContextType.Guild)]
|
||||||
|
public async Task Cp(IUserMessage umsg)
|
||||||
{
|
{
|
||||||
tag = tag?.Trim() ?? "";
|
var channel = (ITextChannel)umsg.Channel;
|
||||||
var link = await GetKonachanImageLink(tag).ConfigureAwait(false);
|
|
||||||
if (string.IsNullOrWhiteSpace(link))
|
await channel.SendMessageAsync("http://i.imgur.com/MZkY1md.jpg").ConfigureAwait(false);
|
||||||
await Context.Channel.SendErrorAsync("No results found.").ConfigureAwait(false);
|
|
||||||
else
|
|
||||||
await Context.Channel.SendMessageAsync(link).ConfigureAwait(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
public async Task Gelbooru([Remainder] string tag = null)
|
[RequireContext(ContextType.Guild)]
|
||||||
{
|
public async Task Boobs(IUserMessage umsg)
|
||||||
tag = tag?.Trim() ?? "";
|
|
||||||
var link = await GetGelbooruImageLink(tag).ConfigureAwait(false);
|
|
||||||
if (string.IsNullOrWhiteSpace(link))
|
|
||||||
await Context.Channel.SendErrorAsync("No results found.").ConfigureAwait(false);
|
|
||||||
else
|
|
||||||
await Context.Channel.SendMessageAsync(link).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
|
||||||
public async Task Rule34([Remainder] string tag = null)
|
|
||||||
{
|
|
||||||
tag = tag?.Trim() ?? "";
|
|
||||||
var link = await GetRule34ImageLink(tag).ConfigureAwait(false);
|
|
||||||
if (string.IsNullOrWhiteSpace(link))
|
|
||||||
await Context.Channel.SendErrorAsync("No results found.").ConfigureAwait(false);
|
|
||||||
else
|
|
||||||
await Context.Channel.SendMessageAsync(link).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
|
||||||
public async Task E621([Remainder] string tag = null)
|
|
||||||
{
|
|
||||||
tag = tag?.Trim() ?? "";
|
|
||||||
var link = await GetE621ImageLink(tag).ConfigureAwait(false);
|
|
||||||
if (string.IsNullOrWhiteSpace(link))
|
|
||||||
await Context.Channel.SendErrorAsync("No results found.").ConfigureAwait(false);
|
|
||||||
else
|
|
||||||
await Context.Channel.SendMessageAsync(link).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
|
||||||
public async Task Cp()
|
|
||||||
{
|
|
||||||
await Context.Channel.SendMessageAsync("http://i.imgur.com/MZkY1md.jpg").ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
|
||||||
public async Task Boobs()
|
|
||||||
{
|
{
|
||||||
|
var channel = (ITextChannel)umsg.Channel;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
JToken obj;
|
JToken obj;
|
||||||
@ -173,17 +213,20 @@ namespace NadekoBot.Modules.NSFW
|
|||||||
{
|
{
|
||||||
obj = JArray.Parse(await http.GetStringAsync($"http://api.oboobs.ru/boobs/{ new NadekoRandom().Next(0, 10229) }").ConfigureAwait(false))[0];
|
obj = JArray.Parse(await http.GetStringAsync($"http://api.oboobs.ru/boobs/{ new NadekoRandom().Next(0, 10229) }").ConfigureAwait(false))[0];
|
||||||
}
|
}
|
||||||
await Context.Channel.SendMessageAsync($"http://media.oboobs.ru/{ obj["preview"].ToString() }").ConfigureAwait(false);
|
await channel.SendMessageAsync($"http://media.oboobs.ru/{ obj["preview"].ToString() }").ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await Context.Channel.SendErrorAsync(ex.Message).ConfigureAwait(false);
|
await channel.SendErrorAsync(ex.Message).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
public async Task Butts()
|
[RequireContext(ContextType.Guild)]
|
||||||
|
public async Task Butts(IUserMessage umsg)
|
||||||
{
|
{
|
||||||
|
var channel = (ITextChannel)umsg.Channel;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
JToken obj;
|
JToken obj;
|
||||||
@ -191,107 +234,69 @@ namespace NadekoBot.Modules.NSFW
|
|||||||
{
|
{
|
||||||
obj = JArray.Parse(await http.GetStringAsync($"http://api.obutts.ru/butts/{ new NadekoRandom().Next(0, 4222) }").ConfigureAwait(false))[0];
|
obj = JArray.Parse(await http.GetStringAsync($"http://api.obutts.ru/butts/{ new NadekoRandom().Next(0, 4222) }").ConfigureAwait(false))[0];
|
||||||
}
|
}
|
||||||
await Context.Channel.SendMessageAsync($"http://media.obutts.ru/{ obj["preview"].ToString() }").ConfigureAwait(false);
|
await channel.SendMessageAsync($"http://media.obutts.ru/{ obj["preview"].ToString() }").ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await Context.Channel.SendErrorAsync(ex.Message).ConfigureAwait(false);
|
await channel.SendErrorAsync(ex.Message).ConfigureAwait(false);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task<string> GetKonachanImageLink(string tag)
|
|
||||||
{
|
|
||||||
var rng = new NadekoRandom();
|
|
||||||
|
|
||||||
var link = $"http://konachan.com/post?" +
|
|
||||||
$"page={rng.Next(0, 5)}";
|
|
||||||
if (!string.IsNullOrWhiteSpace(tag))
|
|
||||||
link += $"&tags={tag.Replace(" ", "_")}";
|
|
||||||
using (var http = new HttpClient())
|
|
||||||
{
|
|
||||||
var webpage = await http.GetStringAsync(link).ConfigureAwait(false);
|
|
||||||
var matches = Regex.Matches(webpage, "<a class=\"directlink largeimg\" href=\"(?<ll>.*?)\">");
|
|
||||||
|
|
||||||
if (matches.Count == 0)
|
|
||||||
return null;
|
|
||||||
return "http:" + matches[rng.Next(0, matches.Count)].Groups["ll"].Value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<string> GetDanbooruImageLink(string tag)
|
public static async Task<string> GetDanbooruImageLink(string tag)
|
||||||
{
|
|
||||||
var rng = new NadekoRandom();
|
|
||||||
|
|
||||||
if (tag == "loli") //loli doesn't work for some reason atm
|
|
||||||
tag = "flat_chest";
|
|
||||||
|
|
||||||
var link = $"http://danbooru.donmai.us/posts?" +
|
|
||||||
$"page={rng.Next(0, 15)}";
|
|
||||||
if (!string.IsNullOrWhiteSpace(tag))
|
|
||||||
link += $"&tags={tag.Replace(" ", "_")}";
|
|
||||||
using (var http = new HttpClient())
|
|
||||||
{
|
|
||||||
var webpage = await http.GetStringAsync(link).ConfigureAwait(false);
|
|
||||||
var matches = Regex.Matches(webpage, "data-large-file-url=\"(?<id>.*?)\"");
|
|
||||||
|
|
||||||
if (matches.Count == 0)
|
|
||||||
return null;
|
|
||||||
return $"http://danbooru.donmai.us" +
|
|
||||||
$"{matches[rng.Next(0, matches.Count)].Groups["id"].Value}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task<string> GetGelbooruImageLink(string tag)
|
|
||||||
{
|
|
||||||
using (var http = new HttpClient())
|
|
||||||
{
|
|
||||||
http.AddFakeHeaders();
|
|
||||||
|
|
||||||
var webpage = await http.GetStringAsync("http://gelbooru.com/index.php?page=dapi&s=post&q=index&limit=100&tags="+ tag.Replace(" ", "_")).ConfigureAwait(false);
|
|
||||||
var matches = Regex.Matches(webpage, "file_url=\"(?<url>.*?)\"");
|
|
||||||
if (matches.Count == 0)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
var rng = new NadekoRandom();
|
|
||||||
var match = matches[rng.Next(0, matches.Count)];
|
|
||||||
return matches[rng.Next(0, matches.Count)].Groups["url"].Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task<string> GetRule34ImageLink(string tag)
|
|
||||||
{
|
|
||||||
var rng = new NadekoRandom();
|
|
||||||
var url =
|
|
||||||
$"http://rule34.xxx/index.php?page=dapi&s=post&q=index&limit=100&tags={tag.Replace(" ", "_")}";
|
|
||||||
using (var http = new HttpClient())
|
|
||||||
{
|
|
||||||
var webpage = await http.GetStringAsync(url).ConfigureAwait(false);
|
|
||||||
var matches = Regex.Matches(webpage, "file_url=\"(?<url>.*?)\"");
|
|
||||||
if (matches.Count == 0)
|
|
||||||
return null;
|
|
||||||
var match = matches[rng.Next(0, matches.Count)];
|
|
||||||
return "http:" + matches[rng.Next(0, matches.Count)].Groups["url"].Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static async Task<string> GetE621ImageLink(string tags)
|
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using (var http = new HttpClient())
|
using (var http = new HttpClient())
|
||||||
{
|
{
|
||||||
http.AddFakeHeaders();
|
http.AddFakeHeaders();
|
||||||
var data = await http.GetStreamAsync("http://e621.net/post/index.xml?tags=" + Uri.EscapeUriString(tags) + "%20order:random&limit=1");
|
var data = await http.GetStreamAsync("https://danbooru.donmai.us/posts.xml?limit=100&tags=" + tag);
|
||||||
var doc = XDocument.Load(data);
|
var doc = new XmlDocument();
|
||||||
return doc.Descendants("file_url").FirstOrDefault().Value;
|
doc.Load(data);
|
||||||
|
var nodes = doc.GetElementsByTagName("file-url");
|
||||||
|
|
||||||
|
var node = nodes[new NadekoRandom().Next(0, nodes.Count)];
|
||||||
|
return "https://danbooru.donmai.us" + node.InnerText;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch
|
||||||
{
|
{
|
||||||
Console.WriteLine("Error in e621 search: \n" + ex);
|
return null;
|
||||||
return "Error, do you have too many tags?";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static async Task<string> GetE621ImageLink(string tag)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var http = new HttpClient())
|
||||||
|
{
|
||||||
|
http.AddFakeHeaders();
|
||||||
|
var data = await http.GetStreamAsync("http://e621.net/post/index.xml?tags=" + tag);
|
||||||
|
var doc = new XmlDocument();
|
||||||
|
doc.Load(data);
|
||||||
|
var nodes = doc.GetElementsByTagName("file_url");
|
||||||
|
|
||||||
|
var node = nodes[new NadekoRandom().Next(0, nodes.Count)];
|
||||||
|
return node.InnerText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Task<string> GetYandereImageLink(string tag) =>
|
||||||
|
Searches.Searches.InternalDapiSearch(tag, Searches.Searches.DapiSearchType.Yandere);
|
||||||
|
|
||||||
|
public static Task<string> GetKonachanImageLink(string tag) =>
|
||||||
|
Searches.Searches.InternalDapiSearch(tag, Searches.Searches.DapiSearchType.Konachan);
|
||||||
|
|
||||||
|
public static Task<string> GetGelbooruImageLink(string tag) =>
|
||||||
|
Searches.Searches.InternalDapiSearch(tag, Searches.Searches.DapiSearchType.Gelbooru);
|
||||||
|
|
||||||
|
public static Task<string> GetRule34ImageLink(string tag) =>
|
||||||
|
Searches.Searches.InternalDapiSearch(tag, Searches.Searches.DapiSearchType.Rule34);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -103,7 +103,10 @@ namespace NadekoBot.Modules.Permissions
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await Context.Channel.SendConfirmAsync($"Blacklisted a `{type}` with id `{id}`").ConfigureAwait(false);
|
if(action == AddRemove.Add)
|
||||||
|
await Context.Channel.SendConfirmAsync($"Blacklisted a `{type}` with id `{id}`").ConfigureAwait(false);
|
||||||
|
else
|
||||||
|
await Context.Channel.SendConfirmAsync($"Unblacklisted a `{type}` with id `{id}`").ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,11 +28,8 @@ namespace NadekoBot.Modules.Permissions
|
|||||||
|
|
||||||
static CmdCdsCommands()
|
static CmdCdsCommands()
|
||||||
{
|
{
|
||||||
using (var uow = DbHandler.UnitOfWork())
|
var configs = NadekoBot.AllGuildConfigs;
|
||||||
{
|
commandCooldowns = new ConcurrentDictionary<ulong, ConcurrentHashSet<CommandCooldown>>(configs.ToDictionary(k => k.GuildId, v => new ConcurrentHashSet<CommandCooldown>(v.CommandCooldowns)));
|
||||||
var configs = NadekoBot.AllGuildConfigs;
|
|
||||||
commandCooldowns = new ConcurrentDictionary<ulong, ConcurrentHashSet<CommandCooldown>>(configs.ToDictionary(k => k.GuildId, v => new ConcurrentHashSet<CommandCooldown>(v.CommandCooldowns)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
using Discord.Commands;
|
using Discord.Commands;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using NadekoBot.Attributes;
|
using NadekoBot.Attributes;
|
||||||
|
using NadekoBot.Extensions;
|
||||||
using NadekoBot.Services;
|
using NadekoBot.Services;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@ -77,12 +78,12 @@ namespace NadekoBot.Modules.Permissions
|
|||||||
if (enabled)
|
if (enabled)
|
||||||
{
|
{
|
||||||
InviteFilteringServers.Add(channel.Guild.Id);
|
InviteFilteringServers.Add(channel.Guild.Id);
|
||||||
await channel.SendMessageAsync("✅ `Invite filtering enabled on this server.`").ConfigureAwait(false);
|
await channel.SendConfirmAsync("Invite filtering enabled on this server.").ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
InviteFilteringServers.TryRemove(channel.Guild.Id);
|
InviteFilteringServers.TryRemove(channel.Guild.Id);
|
||||||
await channel.SendMessageAsync("ℹ️ `Invite filtering disabled on this server.`").ConfigureAwait(false);
|
await channel.SendConfirmAsync("Invite filtering disabled on this server.").ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,12 +111,12 @@ namespace NadekoBot.Modules.Permissions
|
|||||||
if (removed == 0)
|
if (removed == 0)
|
||||||
{
|
{
|
||||||
InviteFilteringChannels.Add(channel.Id);
|
InviteFilteringChannels.Add(channel.Id);
|
||||||
await channel.SendMessageAsync("✅ `Invite filtering enabled on this channel.`").ConfigureAwait(false);
|
await channel.SendConfirmAsync("Invite filtering enabled on this channel.").ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
InviteFilteringChannels.TryRemove(channel.Id);
|
InviteFilteringChannels.TryRemove(channel.Id);
|
||||||
await channel.SendMessageAsync("ℹ️ `Invite filtering disabled on this channel.`").ConfigureAwait(false);
|
await channel.SendConfirmAsync("Invite filtering disabled on this channel.").ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,12 +137,12 @@ namespace NadekoBot.Modules.Permissions
|
|||||||
if (enabled)
|
if (enabled)
|
||||||
{
|
{
|
||||||
WordFilteringServers.Add(channel.Guild.Id);
|
WordFilteringServers.Add(channel.Guild.Id);
|
||||||
await channel.SendMessageAsync("✅ `Word filtering enabled on this server.`").ConfigureAwait(false);
|
await channel.SendConfirmAsync("Word filtering enabled on this server.").ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
WordFilteringServers.TryRemove(channel.Guild.Id);
|
WordFilteringServers.TryRemove(channel.Guild.Id);
|
||||||
await channel.SendMessageAsync("ℹ️ `Word filtering disabled on this server.`").ConfigureAwait(false);
|
await channel.SendConfirmAsync("Word filtering disabled on this server.").ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,12 +170,12 @@ namespace NadekoBot.Modules.Permissions
|
|||||||
if (removed == 0)
|
if (removed == 0)
|
||||||
{
|
{
|
||||||
WordFilteringChannels.Add(channel.Id);
|
WordFilteringChannels.Add(channel.Id);
|
||||||
await channel.SendMessageAsync("✅ `Word filtering enabled on this channel.`").ConfigureAwait(false);
|
await channel.SendConfirmAsync("Word filtering enabled on this channel.").ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
WordFilteringChannels.TryRemove(channel.Id);
|
WordFilteringChannels.TryRemove(channel.Id);
|
||||||
await channel.SendMessageAsync("ℹ️ `Word filtering disabled on this channel.`").ConfigureAwait(false);
|
await channel.SendConfirmAsync("Word filtering disabled on this channel.").ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -207,13 +208,13 @@ namespace NadekoBot.Modules.Permissions
|
|||||||
if (removed == 0)
|
if (removed == 0)
|
||||||
{
|
{
|
||||||
filteredWords.Add(word);
|
filteredWords.Add(word);
|
||||||
await channel.SendMessageAsync($"✅ Word `{word}` successfully added to the list of filtered words.")
|
await channel.SendConfirmAsync($"Word `{word}` successfully added to the list of filtered words.")
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
filteredWords.TryRemove(word);
|
filteredWords.TryRemove(word);
|
||||||
await channel.SendMessageAsync($"ℹ️ Word `{word}` removed from the list of filtered words.")
|
await channel.SendConfirmAsync($"Word `{word}` removed from the list of filtered words.")
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -227,7 +228,7 @@ namespace NadekoBot.Modules.Permissions
|
|||||||
ConcurrentHashSet<string> filteredWords;
|
ConcurrentHashSet<string> filteredWords;
|
||||||
ServerFilteredWords.TryGetValue(channel.Guild.Id, out filteredWords);
|
ServerFilteredWords.TryGetValue(channel.Guild.Id, out filteredWords);
|
||||||
|
|
||||||
await channel.SendMessageAsync($"ℹ️ `List of banned words:`\n" + string.Join(",\n", filteredWords))
|
await channel.SendConfirmAsync($"List of filtered words", string.Join("\n", filteredWords))
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,9 @@ namespace NadekoBot.Modules.Permissions
|
|||||||
|
|
||||||
static Permissions()
|
static Permissions()
|
||||||
{
|
{
|
||||||
|
var _log = LogManager.GetCurrentClassLogger();
|
||||||
|
var sw = Stopwatch.StartNew();
|
||||||
|
|
||||||
using (var uow = DbHandler.UnitOfWork())
|
using (var uow = DbHandler.UnitOfWork())
|
||||||
{
|
{
|
||||||
Cache = new ConcurrentDictionary<ulong, PermissionCache>(uow.GuildConfigs
|
Cache = new ConcurrentDictionary<ulong, PermissionCache>(uow.GuildConfigs
|
||||||
@ -39,6 +42,9 @@ namespace NadekoBot.Modules.Permissions
|
|||||||
PermRole = v.PermissionRole
|
PermRole = v.PermissionRole
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sw.Stop();
|
||||||
|
_log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s");
|
||||||
}
|
}
|
||||||
|
|
||||||
public Permissions() : base()
|
public Permissions() : base()
|
||||||
|
@ -45,7 +45,8 @@ namespace NadekoBot.Modules.Searches
|
|||||||
anilistToken = JObject.Parse(stringContent)["access_token"].ToString();
|
anilistToken = JObject.Parse(stringContent)["access_token"].ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex) {
|
catch (Exception ex)
|
||||||
|
{
|
||||||
_log.Error(ex);
|
_log.Error(ex);
|
||||||
}
|
}
|
||||||
}, null, TimeSpan.FromSeconds(0), TimeSpan.FromMinutes(29));
|
}, null, TimeSpan.FromSeconds(0), TimeSpan.FromMinutes(29));
|
||||||
@ -67,15 +68,17 @@ namespace NadekoBot.Modules.Searches
|
|||||||
await Context.Channel.SendErrorAsync("Failed finding that animu.").ConfigureAwait(false);
|
await Context.Channel.SendErrorAsync("Failed finding that animu.").ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var embed = new EmbedBuilder().WithColor(NadekoBot.OkColor)
|
var embed = new EmbedBuilder().WithColor(NadekoBot.OkColor)
|
||||||
.WithDescription(animeData.Synopsis)
|
.WithDescription(animeData.Synopsis.Replace("<br>", Environment.NewLine))
|
||||||
.WithTitle(animeData.title_english)
|
.WithTitle(animeData.title_english)
|
||||||
.WithUrl(animeData.Link)
|
.WithUrl(animeData.Link)
|
||||||
.WithImageUrl(animeData.image_url_lge)
|
.WithImageUrl(animeData.image_url_lge)
|
||||||
.AddField(efb => efb.WithName("Episodes").WithValue(animeData.total_episodes.ToString()).WithIsInline(true))
|
.AddField(efb => efb.WithName("Episodes").WithValue(animeData.total_episodes.ToString()).WithIsInline(true))
|
||||||
.AddField(efb => efb.WithName("Status").WithValue(animeData.AiringStatus.ToString()).WithIsInline(true))
|
.AddField(efb => efb.WithName("Status").WithValue(animeData.AiringStatus.ToString()).WithIsInline(true))
|
||||||
.AddField(efb => efb.WithName("Genres").WithValue(String.Join(", ", animeData.Genres)).WithIsInline(true));
|
.AddField(efb => efb.WithName("Genres").WithValue(String.Join(", ", animeData.Genres)).WithIsInline(true))
|
||||||
await channel.EmbedAsync(embed).ConfigureAwait(false);
|
.WithFooter(efb => efb.WithText("Score: " + animeData.average_score + " / 100"));
|
||||||
|
await channel.EmbedAsync(embed.Build()).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
@ -96,13 +99,14 @@ namespace NadekoBot.Modules.Searches
|
|||||||
}
|
}
|
||||||
|
|
||||||
var embed = new EmbedBuilder().WithColor(NadekoBot.OkColor)
|
var embed = new EmbedBuilder().WithColor(NadekoBot.OkColor)
|
||||||
.WithDescription(mangaData.Synopsis)
|
.WithDescription(mangaData.Synopsis.Replace("<br>", Environment.NewLine))
|
||||||
.WithTitle(mangaData.title_english)
|
.WithTitle(mangaData.title_english)
|
||||||
.WithUrl(mangaData.Link)
|
.WithUrl(mangaData.Link)
|
||||||
.WithImageUrl(mangaData.image_url_lge)
|
.WithImageUrl(mangaData.image_url_lge)
|
||||||
.AddField(efb => efb.WithName("Episodes").WithValue(mangaData.total_chapters.ToString()).WithIsInline(true))
|
.AddField(efb => efb.WithName("Episodes").WithValue(mangaData.total_chapters.ToString()).WithIsInline(true))
|
||||||
.AddField(efb => efb.WithName("Status").WithValue(mangaData.publishing_status.ToString()).WithIsInline(true))
|
.AddField(efb => efb.WithName("Status").WithValue(mangaData.publishing_status.ToString()).WithIsInline(true))
|
||||||
.AddField(efb => efb.WithName("Genres").WithValue(String.Join(", ", mangaData.Genres)).WithIsInline(true));
|
.AddField(efb => efb.WithName("Genres").WithValue(String.Join(", ", mangaData.Genres)).WithIsInline(true))
|
||||||
|
.WithFooter(efb => efb.WithText("Score: " + mangaData.average_score + " / 100"));
|
||||||
|
|
||||||
await channel.EmbedAsync(embed).ConfigureAwait(false);
|
await channel.EmbedAsync(embed).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
@ -124,7 +128,8 @@ namespace NadekoBot.Modules.Searches
|
|||||||
return await Task.Run(() => { try { return JsonConvert.DeserializeObject<AnimeResult>(aniData); } catch { return null; } }).ConfigureAwait(false);
|
return await Task.Run(() => { try { return JsonConvert.DeserializeObject<AnimeResult>(aniData); } catch { return null; } }).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex) {
|
catch (Exception ex)
|
||||||
|
{
|
||||||
_log.Warn(ex, "Failed anime search for {0}", query);
|
_log.Warn(ex, "Failed anime search for {0}", query);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ using Newtonsoft.Json;
|
|||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using NLog;
|
using NLog;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
@ -26,6 +27,7 @@ namespace NadekoBot.Modules.Searches
|
|||||||
static JokeCommands()
|
static JokeCommands()
|
||||||
{
|
{
|
||||||
_log = LogManager.GetCurrentClassLogger();
|
_log = LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
if (File.Exists("data/wowjokes.json"))
|
if (File.Exists("data/wowjokes.json"))
|
||||||
{
|
{
|
||||||
wowJokes = JsonConvert.DeserializeObject<List<WoWJoke>>(File.ReadAllText("data/wowjokes.json"));
|
wowJokes = JsonConvert.DeserializeObject<List<WoWJoke>>(File.ReadAllText("data/wowjokes.json"));
|
||||||
|
@ -44,7 +44,7 @@ namespace NadekoBot.Modules.Searches
|
|||||||
$"limit={showCount}")
|
$"limit={showCount}")
|
||||||
.ConfigureAwait(false))["data"] as JArray;
|
.ConfigureAwait(false))["data"] as JArray;
|
||||||
var dataList = data.Distinct(new ChampionNameComparer()).Take(showCount).ToList();
|
var dataList = data.Distinct(new ChampionNameComparer()).Take(showCount).ToList();
|
||||||
var eb = new EmbedBuilder().WithColor(NadekoBot.OkColor).WithTitle(Format.Underline($"{dataList.Count} most banned champions"));
|
var eb = new EmbedBuilder().WithOkColor().WithTitle(Format.Underline($"{dataList.Count} most banned champions"));
|
||||||
for (var i = 0; i < dataList.Count; i++)
|
for (var i = 0; i < dataList.Count; i++)
|
||||||
{
|
{
|
||||||
var champ = dataList[i];
|
var champ = dataList[i];
|
||||||
|
@ -12,6 +12,8 @@ namespace NadekoBot.Modules.Searches.Models
|
|||||||
public string description;
|
public string description;
|
||||||
public string image_url_lge;
|
public string image_url_lge;
|
||||||
public string[] Genres;
|
public string[] Genres;
|
||||||
|
public string average_score;
|
||||||
|
|
||||||
public string Link => "http://anilist.co/anime/" + id;
|
public string Link => "http://anilist.co/anime/" + id;
|
||||||
public string Synopsis => description?.Substring(0, description.Length > 500 ? 500 : description.Length) + "...";
|
public string Synopsis => description?.Substring(0, description.Length > 500 ? 500 : description.Length) + "...";
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,43 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace NadekoBot.Modules.Searches.Commands.Models
|
||||||
|
{
|
||||||
|
public class Audio
|
||||||
|
{
|
||||||
|
public string url { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Example
|
||||||
|
{
|
||||||
|
public List<Audio> audio { get; set; }
|
||||||
|
public string text { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GramaticalInfo
|
||||||
|
{
|
||||||
|
public string type { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Sens
|
||||||
|
{
|
||||||
|
public object Definition { get; set; }
|
||||||
|
public List<Example> Examples { get; set; }
|
||||||
|
public GramaticalInfo Gramatical_info { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Result
|
||||||
|
{
|
||||||
|
public string Part_of_speech { get; set; }
|
||||||
|
public List<Sens> Senses { get; set; }
|
||||||
|
public string Url { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DefineModel
|
||||||
|
{
|
||||||
|
public List<Result> Results { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace NadekoBot.Modules.Searches.Commands.Models
|
||||||
|
{
|
||||||
|
public struct GoogleSearchResult
|
||||||
|
{
|
||||||
|
public string Title { get; }
|
||||||
|
public string Link { get; }
|
||||||
|
public string Text { get; }
|
||||||
|
|
||||||
|
public GoogleSearchResult(string title, string link, string text)
|
||||||
|
{
|
||||||
|
this.Title = title;
|
||||||
|
this.Link = link;
|
||||||
|
this.Text = text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -10,6 +10,7 @@ namespace NadekoBot.Modules.Searches.Models
|
|||||||
public int total_volumes;
|
public int total_volumes;
|
||||||
public string description;
|
public string description;
|
||||||
public string[] Genres;
|
public string[] Genres;
|
||||||
|
public string average_score;
|
||||||
public string Link => "http://anilist.co/manga/" + id;
|
public string Link => "http://anilist.co/manga/" + id;
|
||||||
public string Synopsis => description?.Substring(0, description.Length > 500 ? 500 : description.Length) + "...";
|
public string Synopsis => description?.Substring(0, description.Length > 500 ? 500 : description.Length) + "...";
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,67 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace NadekoBot.Modules.Searches.Commands.Models
|
||||||
|
{
|
||||||
|
public class Coord
|
||||||
|
{
|
||||||
|
public double lon { get; set; }
|
||||||
|
public double lat { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Weather
|
||||||
|
{
|
||||||
|
public int id { get; set; }
|
||||||
|
public string main { get; set; }
|
||||||
|
public string description { get; set; }
|
||||||
|
public string icon { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Main
|
||||||
|
{
|
||||||
|
public double temp { get; set; }
|
||||||
|
public int pressure { get; set; }
|
||||||
|
public int humidity { get; set; }
|
||||||
|
public double temp_min { get; set; }
|
||||||
|
public double temp_max { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Wind
|
||||||
|
{
|
||||||
|
public double speed { get; set; }
|
||||||
|
public double deg { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Clouds
|
||||||
|
{
|
||||||
|
public int all { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Sys
|
||||||
|
{
|
||||||
|
public int type { get; set; }
|
||||||
|
public int id { get; set; }
|
||||||
|
public double message { get; set; }
|
||||||
|
public string country { get; set; }
|
||||||
|
public double sunrise { get; set; }
|
||||||
|
public double sunset { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class WeatherData
|
||||||
|
{
|
||||||
|
public Coord coord { get; set; }
|
||||||
|
public List<Weather> weather { get; set; }
|
||||||
|
public Main main { get; set; }
|
||||||
|
public int visibility { get; set; }
|
||||||
|
public Wind wind { get; set; }
|
||||||
|
public Clouds clouds { get; set; }
|
||||||
|
public int dt { get; set; }
|
||||||
|
public Sys sys { get; set; }
|
||||||
|
public int id { get; set; }
|
||||||
|
public string name { get; set; }
|
||||||
|
public int cod { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,6 @@
|
|||||||
using Discord;
|
using Discord;
|
||||||
|
using Discord.API;
|
||||||
|
using NadekoBot.Extensions;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using System;
|
using System;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
@ -34,7 +36,7 @@ namespace NadekoBot.Modules.Searches.Commands.OMDB
|
|||||||
public string Poster { get; set; }
|
public string Poster { get; set; }
|
||||||
|
|
||||||
public EmbedBuilder GetEmbed() =>
|
public EmbedBuilder GetEmbed() =>
|
||||||
new EmbedBuilder().WithColor(NadekoBot.OkColor)
|
new EmbedBuilder().WithOkColor()
|
||||||
.WithTitle(Title)
|
.WithTitle(Title)
|
||||||
.WithUrl($"http://www.imdb.com/title/{ImdbId}/")
|
.WithUrl($"http://www.imdb.com/title/{ImdbId}/")
|
||||||
.WithDescription(Plot)
|
.WithDescription(Plot)
|
||||||
|
@ -15,12 +15,19 @@ namespace NadekoBot.Modules.Searches
|
|||||||
[Group]
|
[Group]
|
||||||
public class OverwatchCommands : ModuleBase
|
public class OverwatchCommands : ModuleBase
|
||||||
{
|
{
|
||||||
|
private readonly Logger _log;
|
||||||
|
public OverwatchCommands()
|
||||||
|
{
|
||||||
|
_log = LogManager.GetCurrentClassLogger();
|
||||||
|
}
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
public async Task Overwatch(string region, [Remainder] string query = null)
|
public async Task Overwatch(string region, [Remainder] string query = null)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(query))
|
if (string.IsNullOrWhiteSpace(query))
|
||||||
return;
|
return;
|
||||||
var battletag = query.Replace("#", "-");
|
var battletag = Regex.Replace(query, "#", "-", RegexOptions.IgnoreCase);
|
||||||
|
|
||||||
|
await channel.TriggerTypingAsync().ConfigureAwait(false);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await Context.Channel.TriggerTypingAsync().ConfigureAwait(false);
|
await Context.Channel.TriggerTypingAsync().ConfigureAwait(false);
|
||||||
@ -28,6 +35,7 @@ namespace NadekoBot.Modules.Searches
|
|||||||
|
|
||||||
var rankimg = $"{model.Competitive.rank_img}";
|
var rankimg = $"{model.Competitive.rank_img}";
|
||||||
var rank = $"{model.Competitive.rank}";
|
var rank = $"{model.Competitive.rank}";
|
||||||
|
var competitiveplay = $"{model.Games.Competitive.played}";
|
||||||
if (string.IsNullOrWhiteSpace(rank))
|
if (string.IsNullOrWhiteSpace(rank))
|
||||||
{
|
{
|
||||||
var embed = new EmbedBuilder()
|
var embed = new EmbedBuilder()
|
||||||
@ -43,7 +51,7 @@ namespace NadekoBot.Modules.Searches
|
|||||||
.AddField(fb => fb.WithName("**Competitive Rank**").WithValue("0").WithIsInline(true))
|
.AddField(fb => fb.WithName("**Competitive Rank**").WithValue("0").WithIsInline(true))
|
||||||
.AddField(fb => fb.WithName("**Competitive Playtime**").WithValue($"{model.Playtime.competitive}").WithIsInline(true))
|
.AddField(fb => fb.WithName("**Competitive Playtime**").WithValue($"{model.Playtime.competitive}").WithIsInline(true))
|
||||||
.AddField(fb => fb.WithName("**Quick Playtime**").WithValue($"{model.Playtime.quick}").WithIsInline(true))
|
.AddField(fb => fb.WithName("**Quick Playtime**").WithValue($"{model.Playtime.quick}").WithIsInline(true))
|
||||||
.WithColor(NadekoBot.OkColor);
|
.WithOkColor();
|
||||||
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
|
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -6,6 +6,7 @@ using NadekoBot.Modules.Searches.Models;
|
|||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using NLog;
|
using NLog;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@ -28,6 +29,7 @@ namespace NadekoBot.Modules.Searches
|
|||||||
static PokemonSearchCommands()
|
static PokemonSearchCommands()
|
||||||
{
|
{
|
||||||
_log = LogManager.GetCurrentClassLogger();
|
_log = LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
if (File.Exists(PokemonListFile))
|
if (File.Exists(PokemonListFile))
|
||||||
{
|
{
|
||||||
pokemons = JsonConvert.DeserializeObject<Dictionary<string, SearchPokemon>>(File.ReadAllText(PokemonListFile));
|
pokemons = JsonConvert.DeserializeObject<Dictionary<string, SearchPokemon>>(File.ReadAllText(PokemonListFile));
|
||||||
@ -52,7 +54,7 @@ namespace NadekoBot.Modules.Searches
|
|||||||
if (kvp.Key.ToUpperInvariant() == pokemon.ToUpperInvariant())
|
if (kvp.Key.ToUpperInvariant() == pokemon.ToUpperInvariant())
|
||||||
{
|
{
|
||||||
var p = kvp.Value;
|
var p = kvp.Value;
|
||||||
await Context.Channel.EmbedAsync(new EmbedBuilder().WithColor(NadekoBot.OkColor)
|
await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||||
.WithTitle(kvp.Key.ToTitleCase())
|
.WithTitle(kvp.Key.ToTitleCase())
|
||||||
.WithDescription(p.BaseStats.ToString())
|
.WithDescription(p.BaseStats.ToString())
|
||||||
.AddField(efb => efb.WithName("Types").WithValue(string.Join(",\n", p.Types)).WithIsInline(true))
|
.AddField(efb => efb.WithName("Types").WithValue(string.Join(",\n", p.Types)).WithIsInline(true))
|
||||||
@ -75,7 +77,7 @@ namespace NadekoBot.Modules.Searches
|
|||||||
{
|
{
|
||||||
if (kvp.Key.ToUpperInvariant() == ability)
|
if (kvp.Key.ToUpperInvariant() == ability)
|
||||||
{
|
{
|
||||||
await Context.Channel.EmbedAsync(new EmbedBuilder().WithColor(NadekoBot.OkColor)
|
await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||||
.WithTitle(kvp.Value.Name)
|
.WithTitle(kvp.Value.Name)
|
||||||
.WithDescription(kvp.Value.Desc)
|
.WithDescription(kvp.Value.Desc)
|
||||||
.AddField(efb => efb.WithName("Rating").WithValue(kvp.Value.Rating.ToString()).WithIsInline(true))
|
.AddField(efb => efb.WithName("Rating").WithValue(kvp.Value.Rating.ToString()).WithIsInline(true))
|
||||||
|
@ -14,6 +14,7 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NadekoBot.Extensions;
|
using NadekoBot.Extensions;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
namespace NadekoBot.Modules.Searches
|
namespace NadekoBot.Modules.Searches
|
||||||
{
|
{
|
||||||
@ -75,8 +76,8 @@ namespace NadekoBot.Modules.Searches
|
|||||||
|
|
||||||
static StreamNotificationCommands()
|
static StreamNotificationCommands()
|
||||||
{
|
{
|
||||||
|
_log = LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
_log = NLog.LogManager.GetCurrentClassLogger();
|
|
||||||
checkTimer = new Timer(async (state) =>
|
checkTimer = new Timer(async (state) =>
|
||||||
{
|
{
|
||||||
oldCachedStatuses = new ConcurrentDictionary<string, StreamStatus>(cachedStatuses);
|
oldCachedStatuses = new ConcurrentDictionary<string, StreamStatus>(cachedStatuses);
|
||||||
@ -105,7 +106,13 @@ namespace NadekoBot.Modules.Searches
|
|||||||
var channel = server?.GetTextChannelAsync(fs.ChannelId);
|
var channel = server?.GetTextChannelAsync(fs.ChannelId);
|
||||||
if (channel == null)
|
if (channel == null)
|
||||||
return;
|
return;
|
||||||
try { await (await channel).EmbedAsync(fs.GetEmbed(newStatus)).ConfigureAwait(false); } catch { }
|
try
|
||||||
|
{
|
||||||
|
var msg = await channel.EmbedAsync(fs.GetEmbed(newStatus).Build()).ConfigureAwait(false);
|
||||||
|
if (!newStatus.IsLive)
|
||||||
|
msg.DeleteAfter(60);
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch { }
|
catch { }
|
||||||
|
@ -21,26 +21,22 @@ namespace NadekoBot.Modules.Searches
|
|||||||
[Group]
|
[Group]
|
||||||
public class TranslateCommands : ModuleBase
|
public class TranslateCommands : ModuleBase
|
||||||
{
|
{
|
||||||
private static ConcurrentDictionary<ulong, bool> TranslatedChannels { get; }
|
private static ConcurrentDictionary<ulong, bool> TranslatedChannels { get; } = new ConcurrentDictionary<ulong, bool>();
|
||||||
private static ConcurrentDictionary<UserChannelPair, string> UserLanguages { get; }
|
private static ConcurrentDictionary<UserChannelPair, string> UserLanguages { get; } = new ConcurrentDictionary<UserChannelPair, string>();
|
||||||
|
|
||||||
static TranslateCommands()
|
static TranslateCommands()
|
||||||
{
|
{
|
||||||
TranslatedChannels = new ConcurrentDictionary<ulong, bool>();
|
NadekoBot.Client.MessageReceived += async (msg) =>
|
||||||
UserLanguages = new ConcurrentDictionary<UserChannelPair, string>();
|
|
||||||
|
|
||||||
NadekoBot.Client.MessageReceived += (msg) =>
|
|
||||||
{
|
{
|
||||||
var umsg = msg as SocketUserMessage;
|
try
|
||||||
if(umsg == null)
|
|
||||||
return Task.CompletedTask;
|
|
||||||
|
|
||||||
bool autoDelete;
|
|
||||||
if (!TranslatedChannels.TryGetValue(umsg.Channel.Id, out autoDelete))
|
|
||||||
return Task.CompletedTask;
|
|
||||||
|
|
||||||
var t = Task.Run(async () =>
|
|
||||||
{
|
{
|
||||||
|
var umsg = msg as SocketUserMessage;
|
||||||
|
if (umsg == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
bool autoDelete;
|
||||||
|
if (!TranslatedChannels.TryGetValue(umsg.Channel.Id, out autoDelete))
|
||||||
|
return;
|
||||||
var key = new UserChannelPair()
|
var key = new UserChannelPair()
|
||||||
{
|
{
|
||||||
UserId = umsg.Author.Id,
|
UserId = umsg.Author.Id,
|
||||||
@ -51,18 +47,13 @@ namespace NadekoBot.Modules.Searches
|
|||||||
if (!UserLanguages.TryGetValue(key, out langs))
|
if (!UserLanguages.TryGetValue(key, out langs))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
try
|
var text = await TranslateInternal(langs, umsg.Resolve(UserMentionHandling.Ignore), true)
|
||||||
{
|
.ConfigureAwait(false);
|
||||||
var text = await TranslateInternal(langs, umsg.Resolve(userHandling: TagHandling.Ignore), true)
|
if (autoDelete)
|
||||||
.ConfigureAwait(false);
|
try { await umsg.DeleteAsync().ConfigureAwait(false); } catch { }
|
||||||
if (autoDelete)
|
await umsg.Channel.SendConfirmAsync($"{umsg.Author.Mention} `:` " + text.Replace("<@ ", "<@").Replace("<@! ", "<@!")).ConfigureAwait(false);
|
||||||
try { await umsg.DeleteAsync().ConfigureAwait(false); } catch { }
|
}
|
||||||
await umsg.Channel.SendConfirmAsync($"{umsg.Author.Mention} `:` "+text.Replace("<@ ", "<@").Replace("<@! ", "<@!")).ConfigureAwait(false);
|
catch { }
|
||||||
}
|
|
||||||
catch { }
|
|
||||||
|
|
||||||
});
|
|
||||||
return Task.CompletedTask;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,6 +17,13 @@ using ImageSharp;
|
|||||||
using NadekoBot.Extensions;
|
using NadekoBot.Extensions;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using NadekoBot.Modules.Searches.Commands.OMDB;
|
using NadekoBot.Modules.Searches.Commands.OMDB;
|
||||||
|
using NadekoBot.Modules.Searches.Commands.Models;
|
||||||
|
using AngleSharp.Parser.Html;
|
||||||
|
using AngleSharp;
|
||||||
|
using AngleSharp.Dom.Html;
|
||||||
|
using AngleSharp.Dom;
|
||||||
|
using System.Xml;
|
||||||
|
using System.Xml.Linq;
|
||||||
|
|
||||||
namespace NadekoBot.Modules.Searches
|
namespace NadekoBot.Modules.Searches
|
||||||
{
|
{
|
||||||
@ -25,27 +32,29 @@ namespace NadekoBot.Modules.Searches
|
|||||||
{
|
{
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
public async Task Weather(string city, string country)
|
public async Task Weather([Remainder] string query)
|
||||||
{
|
{
|
||||||
city = city.Replace(" ", "");
|
if (string.IsNullOrWhiteSpace(query))
|
||||||
country = city.Replace(" ", "");
|
return;
|
||||||
|
|
||||||
string response;
|
string response;
|
||||||
using (var http = new HttpClient())
|
using (var http = new HttpClient())
|
||||||
response = await http.GetStringAsync($"http://api.ninetales.us/nadekobot/weather/?city={city}&country={country}").ConfigureAwait(false);
|
response = await http.GetStringAsync($"http://api.openweathermap.org/data/2.5/weather?q={query}&appid=42cd627dd60debf25a5739e50a217d74&units=metric").ConfigureAwait(false);
|
||||||
|
|
||||||
var obj = JObject.Parse(response)["weather"];
|
var data = JsonConvert.DeserializeObject<WeatherData>(response);
|
||||||
|
|
||||||
var embed = new EmbedBuilder()
|
var embed = new EmbedBuilder()
|
||||||
.AddField(fb => fb.WithName("🌍 **Location**").WithValue($"{obj["target"]}").WithIsInline(true))
|
.AddField(fb => fb.WithName("🌍 **Location**").WithValue(data.name + ", " + data.sys.country).WithIsInline(true))
|
||||||
.AddField(fb => fb.WithName("📏 **Lat,Long**").WithValue($"{obj["latitude"]}, {obj["longitude"]}").WithIsInline(true))
|
.AddField(fb => fb.WithName("📏 **Lat,Long**").WithValue($"{data.coord.lat}, {data.coord.lon}").WithIsInline(true))
|
||||||
.AddField(fb => fb.WithName("☁ **Condition**").WithValue($"{obj["condition"]}").WithIsInline(true))
|
.AddField(fb => fb.WithName("☁ **Condition**").WithValue(String.Join(", ", data.weather.Select(w => w.main))).WithIsInline(true))
|
||||||
.AddField(fb => fb.WithName("😓 **Humidity**").WithValue($"{obj["humidity"]}%").WithIsInline(true))
|
.AddField(fb => fb.WithName("😓 **Humidity**").WithValue($"{data.main.humidity}%").WithIsInline(true))
|
||||||
.AddField(fb => fb.WithName("💨 **Wind Speed**").WithValue($"{obj["windspeedk"]}km/h ({obj["windspeedm"]}mph)").WithIsInline(true))
|
.AddField(fb => fb.WithName("💨 **Wind Speed**").WithValue(data.wind.speed + " km/h").WithIsInline(true))
|
||||||
.AddField(fb => fb.WithName("🌡 **Temperature**").WithValue($"{obj["centigrade"]}°C ({obj["fahrenheit"]}°F)").WithIsInline(true))
|
.AddField(fb => fb.WithName("🌡 **Temperature**").WithValue(data.main.temp + "°C").WithIsInline(true))
|
||||||
.AddField(fb => fb.WithName("🔆 **Feels like**").WithValue($"{obj["feelscentigrade"]}°C ({obj["feelsfahrenheit"]}°F)").WithIsInline(true))
|
.AddField(fb => fb.WithName("🔆 **Min - Max**").WithValue($"{data.main.temp_min}°C - {data.main.temp_max}°C").WithIsInline(true))
|
||||||
.AddField(fb => fb.WithName("🌄 **Sunrise**").WithValue($"{obj["sunrise"]}").WithIsInline(true))
|
.AddField(fb => fb.WithName("🌄 **Sunrise (utc)**").WithValue($"{data.sys.sunrise.ToUnixTimestamp():HH:mm}").WithIsInline(true))
|
||||||
.AddField(fb => fb.WithName("🌇 **Sunset**").WithValue($"{obj["sunset"]}").WithIsInline(true))
|
.AddField(fb => fb.WithName("🌇 **Sunset (utc)**").WithValue($"{data.sys.sunset.ToUnixTimestamp():HH:mm}").WithIsInline(true))
|
||||||
.WithColor(NadekoBot.OkColor);
|
.WithOkColor()
|
||||||
|
.WithFooter(efb => efb.WithText("Powered by http://openweathermap.org"));
|
||||||
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
|
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,7 +190,7 @@ namespace NadekoBot.Modules.Searches
|
|||||||
await Context.Channel.SendErrorAsync("Failed to shorten that url.").ConfigureAwait(false);
|
await Context.Channel.SendErrorAsync("Failed to shorten that url.").ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
await Context.Channel.EmbedAsync(new EmbedBuilder().WithColor(NadekoBot.OkColor)
|
await msg.Channel.EmbedAsync(new EmbedBuilder().WithColor(NadekoBot.OkColor)
|
||||||
.AddField(efb => efb.WithName("Original Url")
|
.AddField(efb => efb.WithName("Original Url")
|
||||||
.WithValue($"<{arg}>"))
|
.WithValue($"<{arg}>"))
|
||||||
.AddField(efb => efb.WithName("Short Url")
|
.AddField(efb => efb.WithName("Short Url")
|
||||||
@ -189,6 +198,9 @@ namespace NadekoBot.Modules.Searches
|
|||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//private readonly Regex googleSearchRegex = new Regex(@"<h3 class=""r""><a href=""(?:\/url?q=)?(?<link>.*?)"".*?>(?<title>.*?)<\/a>.*?class=""st"">(?<text>.*?)<\/span>", RegexOptions.Compiled);
|
||||||
|
//private readonly Regex htmlReplace = new Regex(@"(?:<b>(.*?)<\/b>|<em>(.*?)<\/em>)", RegexOptions.Compiled);
|
||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
public async Task Google([Remainder] string terms = null)
|
public async Task Google([Remainder] string terms = null)
|
||||||
{
|
{
|
||||||
@ -196,8 +208,49 @@ namespace NadekoBot.Modules.Searches
|
|||||||
if (string.IsNullOrWhiteSpace(terms))
|
if (string.IsNullOrWhiteSpace(terms))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
await Context.Channel.SendConfirmAsync($"https://google.com/search?q={ WebUtility.UrlEncode(terms).Replace(' ', '+') }")
|
terms = WebUtility.UrlEncode(terms).Replace(' ', '+');
|
||||||
.ConfigureAwait(false);
|
|
||||||
|
var fullQueryLink = $"https://www.google.com/search?q={ terms }&gws_rd=cr,ssl";
|
||||||
|
var config = Configuration.Default.WithDefaultLoader();
|
||||||
|
var document = await BrowsingContext.New(config).OpenAsync(fullQueryLink);
|
||||||
|
|
||||||
|
var elems = document.QuerySelectorAll("div.g");
|
||||||
|
|
||||||
|
var resultsElem = document.QuerySelectorAll("#resultStats").FirstOrDefault();
|
||||||
|
var totalResults = resultsElem?.TextContent;
|
||||||
|
//var time = resultsElem.Children.FirstOrDefault()?.TextContent
|
||||||
|
//^ this doesn't work for some reason, <nobr> is completely missing in parsed collection
|
||||||
|
if (!elems.Any())
|
||||||
|
return;
|
||||||
|
|
||||||
|
var results = elems.Select<IElement, GoogleSearchResult?>(elem =>
|
||||||
|
{
|
||||||
|
var aTag = (elem.Children.FirstOrDefault().Children.FirstOrDefault() as IHtmlAnchorElement); // <h3> -> <a>
|
||||||
|
var href = aTag?.Href;
|
||||||
|
var name = aTag?.TextContent;
|
||||||
|
if (href == null || name == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var txt = elem.QuerySelectorAll(".st").FirstOrDefault()?.TextContent;
|
||||||
|
|
||||||
|
if (txt == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return new GoogleSearchResult(name, href, txt);
|
||||||
|
}).Where(x => x != null).Take(5);
|
||||||
|
|
||||||
|
var embed = new EmbedBuilder()
|
||||||
|
.WithOkColor()
|
||||||
|
.WithAuthor(eab => eab.WithName("Search For: " + terms.TrimTo(50))
|
||||||
|
.WithUrl(fullQueryLink)
|
||||||
|
.WithIconUrl("http://i.imgur.com/G46fm8J.png"))
|
||||||
|
.WithTitle(umsg.Author.Mention)
|
||||||
|
.WithFooter(efb => efb.WithText(totalResults));
|
||||||
|
|
||||||
|
var desc = await Task.WhenAll(results.Select(async res =>
|
||||||
|
$"[{Format.Bold(res?.Title)}]({(await NadekoBot.Google.ShortenUrl(res?.Link))})\n{res?.Text}\n\n"))
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
await Context.Channel.EmbedAsync(embed.WithDescription(String.Concat(desc))).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
@ -228,14 +281,14 @@ namespace NadekoBot.Modules.Searches
|
|||||||
var desc = item["text"].ToString();
|
var desc = item["text"].ToString();
|
||||||
var types = String.Join(",\n", item["types"].ToObject<string[]>());
|
var types = String.Join(",\n", item["types"].ToObject<string[]>());
|
||||||
var img = item["editions"][0]["image_url"].ToString();
|
var img = item["editions"][0]["image_url"].ToString();
|
||||||
var embed = new EmbedBuilder().WithColor(NadekoBot.OkColor)
|
var embed = new EmbedBuilder().WithOkColor()
|
||||||
.WithTitle(item["name"].ToString())
|
.WithTitle(item["name"].ToString())
|
||||||
.WithDescription(desc)
|
.WithDescription(desc)
|
||||||
.WithImageUrl(img)
|
.WithImageUrl(img)
|
||||||
.AddField(efb => efb.WithName("Store Url").WithValue(storeUrl).WithIsInline(true))
|
.AddField(efb => efb.WithName("Store Url").WithValue(storeUrl).WithIsInline(true))
|
||||||
.AddField(efb => efb.WithName("Cost").WithValue(cost).WithIsInline(true))
|
.AddField(efb => efb.WithName("Cost").WithValue(cost).WithIsInline(true))
|
||||||
.AddField(efb => efb.WithName("Types").WithValue(types).WithIsInline(true));
|
.AddField(efb => efb.WithName("Types").WithValue(types).WithIsInline(true));
|
||||||
//.AddField(efb => efb.WithName("Store Url").WithValue(await NadekoBot.Google.ShortenUrl(items[0]["store_url"].ToString())).WithIsInline(true));
|
//.AddField(efb => efb.WithName("Store Url").WithValue(await NadekoBot.Google.ShortenUrl(items[0]["store_url"].ToString())).WithIsInline(true));
|
||||||
|
|
||||||
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
|
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
@ -332,7 +385,7 @@ namespace NadekoBot.Modules.Searches
|
|||||||
.WithUrl("http://www.yodaspeak.co.uk/")
|
.WithUrl("http://www.yodaspeak.co.uk/")
|
||||||
.WithAuthor(au => au.WithName("Yoda").WithIconUrl("http://www.yodaspeak.co.uk/yoda-small1.gif"))
|
.WithAuthor(au => au.WithName("Yoda").WithIconUrl("http://www.yodaspeak.co.uk/yoda-small1.gif"))
|
||||||
.WithDescription(res)
|
.WithDescription(res)
|
||||||
.WithColor(NadekoBot.OkColor);
|
.WithOkColor();
|
||||||
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
|
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
@ -372,7 +425,7 @@ namespace NadekoBot.Modules.Searches
|
|||||||
var word = item["word"].ToString();
|
var word = item["word"].ToString();
|
||||||
var def = item["definition"].ToString();
|
var def = item["definition"].ToString();
|
||||||
var link = item["permalink"].ToString();
|
var link = item["permalink"].ToString();
|
||||||
var embed = new EmbedBuilder().WithColor(NadekoBot.OkColor)
|
var embed = new EmbedBuilder().WithOkColor()
|
||||||
.WithUrl(link)
|
.WithUrl(link)
|
||||||
.WithAuthor(eab => eab.WithIconUrl("http://i.imgur.com/nwERwQE.jpg").WithName(word))
|
.WithAuthor(eab => eab.WithIconUrl("http://i.imgur.com/nwERwQE.jpg").WithName(word))
|
||||||
.WithDescription(def);
|
.WithDescription(def);
|
||||||
@ -386,7 +439,43 @@ namespace NadekoBot.Modules.Searches
|
|||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
public async Task Hashtag([Remainder] string query = null)
|
[RequireContext(ContextType.Guild)]
|
||||||
|
public async Task Define([Remainder] string word)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(word))
|
||||||
|
return;
|
||||||
|
|
||||||
|
using (var http = new HttpClient())
|
||||||
|
{
|
||||||
|
var res = await http.GetStringAsync("http://api.pearson.com/v2/dictionaries/entries?headword=" + WebUtility.UrlEncode(word.Trim())).ConfigureAwait(false);
|
||||||
|
|
||||||
|
var data = JsonConvert.DeserializeObject<DefineModel>(res);
|
||||||
|
|
||||||
|
var sense = data.Results.Where(x => x.Senses != null && x.Senses[0].Definition != null).FirstOrDefault()?.Senses[0];
|
||||||
|
|
||||||
|
if (sense?.Definition == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
string definition = sense.Definition.ToString();
|
||||||
|
if (!(sense.Definition is string))
|
||||||
|
definition = ((JArray)JToken.Parse(sense.Definition.ToString())).First.ToString();
|
||||||
|
|
||||||
|
var embed = new EmbedBuilder().WithOkColor()
|
||||||
|
.WithTitle("Define: " + word)
|
||||||
|
.WithDescription(definition)
|
||||||
|
.WithFooter(efb => efb.WithText(sense.Gramatical_info?.type));
|
||||||
|
|
||||||
|
if (sense.Examples != null)
|
||||||
|
embed.AddField(efb => efb.WithName("Example").WithValue(sense.Examples.First().text));
|
||||||
|
|
||||||
|
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
public async Task Hashtag(IUserMessage umsg, [Remainder] string query = null)
|
||||||
{
|
{
|
||||||
var arg = query;
|
var arg = query;
|
||||||
if (string.IsNullOrWhiteSpace(arg))
|
if (string.IsNullOrWhiteSpace(arg))
|
||||||
@ -416,7 +505,7 @@ namespace NadekoBot.Modules.Searches
|
|||||||
var hashtag = item["hashtag"].ToString();
|
var hashtag = item["hashtag"].ToString();
|
||||||
var link = item["uri"].ToString();
|
var link = item["uri"].ToString();
|
||||||
var desc = item["text"].ToString();
|
var desc = item["text"].ToString();
|
||||||
await Context.Channel.EmbedAsync(new EmbedBuilder().WithColor(NadekoBot.OkColor)
|
await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||||
.WithAuthor(eab => eab.WithUrl(link)
|
.WithAuthor(eab => eab.WithUrl(link)
|
||||||
.WithIconUrl("http://res.cloudinary.com/urbandictionary/image/upload/a_exif,c_fit,h_200,w_200/v1394975045/b8oszuu3tbq7ebyo7vo1.jpg")
|
.WithIconUrl("http://res.cloudinary.com/urbandictionary/image/upload/a_exif,c_fit,h_200,w_200/v1394975045/b8oszuu3tbq7ebyo7vo1.jpg")
|
||||||
.WithName(query))
|
.WithName(query))
|
||||||
@ -461,15 +550,9 @@ namespace NadekoBot.Modules.Searches
|
|||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
public async Task Safebooru([Remainder] string tag = null)
|
[RequireContext(ContextType.Guild)]
|
||||||
{
|
public Task Safebooru(IUserMessage umsg, [Remainder] string tag = null)
|
||||||
tag = tag?.Trim() ?? "";
|
=> InternalDapiCommand(umsg, tag, DapiSearchType.Safebooru);
|
||||||
var link = await GetSafebooruImageLink(tag).ConfigureAwait(false);
|
|
||||||
if (link == null)
|
|
||||||
await Context.Channel.SendErrorAsync("No results.");
|
|
||||||
else
|
|
||||||
await Context.Channel.SendMessageAsync(link).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
public async Task Wiki([Remainder] string query = null)
|
public async Task Wiki([Remainder] string query = null)
|
||||||
@ -529,27 +612,12 @@ namespace NadekoBot.Modules.Searches
|
|||||||
public async Task Avatar([Remainder] IUser usr = null)
|
public async Task Avatar([Remainder] IUser usr = null)
|
||||||
{
|
{
|
||||||
if (usr == null)
|
if (usr == null)
|
||||||
{
|
usr = umsg.Author;
|
||||||
await Context.Channel.SendErrorAsync("Invalid user specified.").ConfigureAwait(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
await Context.Channel.SendMessageAsync(await NadekoBot.Google.ShortenUrl(usr.AvatarUrl).ConfigureAwait(false)).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task<string> GetSafebooruImageLink(string tag)
|
await channel.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||||
{
|
.WithTitle($"{usr}'s Avatar")
|
||||||
var rng = new NadekoRandom();
|
.WithImageUrl(usr.AvatarUrl)
|
||||||
var url =
|
.Build()).ConfigureAwait(false);
|
||||||
$"http://safebooru.org/index.php?page=dapi&s=post&q=index&limit=100&tags={tag.Replace(" ", "_")}";
|
|
||||||
using (var http = new HttpClient())
|
|
||||||
{
|
|
||||||
var webpage = await http.GetStringAsync(url).ConfigureAwait(false);
|
|
||||||
var matches = Regex.Matches(webpage, "file_url=\"(?<url>.*?)\"");
|
|
||||||
if (matches.Count == 0)
|
|
||||||
return null;
|
|
||||||
var match = matches[rng.Next(0, matches.Count)];
|
|
||||||
return "http:" + matches[rng.Next(0, matches.Count)].Groups["url"].Value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
@ -654,7 +722,78 @@ namespace NadekoBot.Modules.Searches
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<bool> ValidateQuery(IMessageChannel ch, string query)
|
public enum DapiSearchType
|
||||||
|
{
|
||||||
|
Safebooru,
|
||||||
|
Gelbooru,
|
||||||
|
Konachan,
|
||||||
|
Rule34,
|
||||||
|
Yandere
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task InternalDapiCommand(IUserMessage umsg, string tag, DapiSearchType type)
|
||||||
|
{
|
||||||
|
var channel = (ITextChannel)umsg.Channel;
|
||||||
|
|
||||||
|
tag = tag?.Trim() ?? "";
|
||||||
|
|
||||||
|
var url = await InternalDapiSearch(tag, type).ConfigureAwait(false);
|
||||||
|
|
||||||
|
if (url == null)
|
||||||
|
await channel.SendErrorAsync(umsg.Author.Mention + " No results.");
|
||||||
|
else
|
||||||
|
await channel.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||||
|
.WithDescription(umsg.Author.Mention + " " + tag)
|
||||||
|
.WithImageUrl(url)
|
||||||
|
.WithFooter(efb => efb.WithText(type.ToString()))
|
||||||
|
.Build()).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<string> InternalDapiSearch(string tag, DapiSearchType type)
|
||||||
|
{
|
||||||
|
tag = tag?.Replace(" ", "_");
|
||||||
|
string website = "";
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case DapiSearchType.Safebooru:
|
||||||
|
website = $"https://safebooru.org/index.php?page=dapi&s=post&q=index&limit=100&tags={tag}";
|
||||||
|
break;
|
||||||
|
case DapiSearchType.Gelbooru:
|
||||||
|
website = $"http://gelbooru.com/index.php?page=dapi&s=post&q=index&limit=100&tags={tag}";
|
||||||
|
break;
|
||||||
|
case DapiSearchType.Rule34:
|
||||||
|
website = $"https://rule34.xxx/index.php?page=dapi&s=post&q=index&limit=100&tags={tag}";
|
||||||
|
break;
|
||||||
|
case DapiSearchType.Konachan:
|
||||||
|
website = $"https://konachan.com/post.xml?s=post&q=index&limit=100&tags={tag}";
|
||||||
|
break;
|
||||||
|
case DapiSearchType.Yandere:
|
||||||
|
website = $"https://yande.re/post.xml?limit=100&tags={tag}";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var http = new HttpClient())
|
||||||
|
{
|
||||||
|
http.AddFakeHeaders();
|
||||||
|
var data = await http.GetStreamAsync(website);
|
||||||
|
var doc = new XmlDocument();
|
||||||
|
doc.Load(data);
|
||||||
|
|
||||||
|
var node = doc.LastChild.ChildNodes[new NadekoRandom().Next(0, doc.LastChild.ChildNodes.Count)];
|
||||||
|
|
||||||
|
var url = node.Attributes["file_url"].Value;
|
||||||
|
if (!url.StartsWith("http"))
|
||||||
|
url = "https:" + url;
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static async Task<bool> ValidateQuery(ITextChannel ch, string query)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(query.Trim())) return true;
|
if (!string.IsNullOrEmpty(query.Trim())) return true;
|
||||||
await ch.SendErrorAsync("Please specify search parameters.").ConfigureAwait(false);
|
await ch.SendErrorAsync("Please specify search parameters.").ConfigureAwait(false);
|
||||||
|
@ -99,5 +99,31 @@ namespace NadekoBot.Modules.Utility
|
|||||||
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
|
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
[OwnerOnly]
|
||||||
|
public async Task Activity(IUserMessage imsg, int page = 1)
|
||||||
|
{
|
||||||
|
const int activityPerPage = 15;
|
||||||
|
page -= 1;
|
||||||
|
|
||||||
|
if (page < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int startCount = page * activityPerPage;
|
||||||
|
|
||||||
|
StringBuilder str = new StringBuilder();
|
||||||
|
foreach (var kvp in NadekoBot.CommandHandler.UserMessagesSent.OrderByDescending(kvp => kvp.Value).Skip(page*activityPerPage).Take(activityPerPage))
|
||||||
|
{
|
||||||
|
str.AppendLine($"`{++startCount}.` **{kvp.Key}** [{kvp.Value/NadekoBot.Stats.GetUptime().TotalSeconds:F2}/s] - {kvp.Value} total");
|
||||||
|
}
|
||||||
|
|
||||||
|
await imsg.Channel.EmbedAsync(new EmbedBuilder().WithTitle($"Activity Page #{page}")
|
||||||
|
.WithOkColor()
|
||||||
|
.WithFooter(efb => efb.WithText($"{NadekoBot.CommandHandler.UserMessagesSent.Count} users total."))
|
||||||
|
.WithDescription(str.ToString())
|
||||||
|
.Build());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -9,6 +9,7 @@ using Newtonsoft.Json;
|
|||||||
using NLog;
|
using NLog;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
@ -30,7 +31,6 @@ namespace NadekoBot.Modules.Utility
|
|||||||
static UnitConverterCommands()
|
static UnitConverterCommands()
|
||||||
{
|
{
|
||||||
_log = LogManager.GetCurrentClassLogger();
|
_log = LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var data = JsonConvert.DeserializeObject<List<MeasurementUnit>>(File.ReadAllText("data/units.json")).Select(u => new ConvertUnit()
|
var data = JsonConvert.DeserializeObject<List<MeasurementUnit>>(File.ReadAllText("data/units.json")).Select(u => new ConvertUnit()
|
||||||
|
@ -9,6 +9,7 @@ using NadekoBot.Extensions;
|
|||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using NadekoBot.Services.Impl;
|
using NadekoBot.Services.Impl;
|
||||||
|
using System.Net.Http;
|
||||||
|
|
||||||
namespace NadekoBot.Modules.Utility
|
namespace NadekoBot.Modules.Utility
|
||||||
{
|
{
|
||||||
@ -23,6 +24,25 @@ namespace NadekoBot.Modules.Utility
|
|||||||
[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
public async Task WhosPlaying([Remainder] string game = null)
|
public async Task WhosPlaying([Remainder] string game = null)
|
||||||
|
public async Task TogetherTube(IUserMessage imsg)
|
||||||
|
{
|
||||||
|
Uri target;
|
||||||
|
using (var http = new HttpClient())
|
||||||
|
{
|
||||||
|
var res = await http.GetAsync("https://togethertube.com/room/create").ConfigureAwait(false);
|
||||||
|
target = res.RequestMessage.RequestUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||||
|
.WithAuthor(eab => eab.WithIconUrl("https://togethertube.com/assets/img/favicons/favicon-32x32.png")
|
||||||
|
.WithName("Together Tube")
|
||||||
|
.WithUrl("https://togethertube.com/"))
|
||||||
|
.WithDescription($"{imsg.Author.Mention} Here is your room link:\n{target}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
public async Task WhosPlaying(IUserMessage umsg, [Remainder] string game = null)
|
||||||
{
|
{
|
||||||
game = game.Trim().ToUpperInvariant();
|
game = game.Trim().ToUpperInvariant();
|
||||||
if (string.IsNullOrWhiteSpace(game))
|
if (string.IsNullOrWhiteSpace(game))
|
||||||
@ -123,16 +143,17 @@ namespace NadekoBot.Modules.Utility
|
|||||||
|
|
||||||
if (page < 1 || page > 100)
|
if (page < 1 || page > 100)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (target != null)
|
if (target != null)
|
||||||
{
|
{
|
||||||
var roles = target.GetRoles().Except(new[] { guild.EveryoneRole }).OrderBy(r => -r.Position).Skip((page - 1) * RolesPerPage).Take(RolesPerPage);
|
var roles = target.GetRoles().Except(new[] { guild.EveryoneRole }).OrderBy(r => -r.Position).Skip((page - 1) * RolesPerPage).Take(RolesPerPage);
|
||||||
if (!roles.Any())
|
if (!roles.Any())
|
||||||
{
|
{
|
||||||
await channel.SendErrorAsync("No roles on this page.");
|
await channel.SendErrorAsync("No roles on this page.").ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await channel.SendConfirmAsync($"⚔ **Page #{page} of roles for {target.Username}**", $"```css\n• " + string.Join("\n• ", roles).SanitizeMentions() + "\n```");
|
await channel.SendConfirmAsync($"⚔ **Page #{page} of roles for {target.Username}**", $"```css\n• " + string.Join("\n• ", roles).SanitizeMentions() + "\n```").ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -140,11 +161,11 @@ namespace NadekoBot.Modules.Utility
|
|||||||
var roles = guild.Roles.Except(new[] { guild.EveryoneRole }).OrderBy(r => -r.Position).Skip((page - 1) * RolesPerPage).Take(RolesPerPage);
|
var roles = guild.Roles.Except(new[] { guild.EveryoneRole }).OrderBy(r => -r.Position).Skip((page - 1) * RolesPerPage).Take(RolesPerPage);
|
||||||
if (!roles.Any())
|
if (!roles.Any())
|
||||||
{
|
{
|
||||||
await channel.SendErrorAsync("No roles on this page.");
|
await channel.SendErrorAsync("No roles on this page.").ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await channel.SendConfirmAsync($"⚔ **Page #{page} of all roles on this server:**", $"```css\n• " + string.Join("\n• ", roles).SanitizeMentions() + "\n```");
|
await channel.SendConfirmAsync($"⚔ **Page #{page} of all roles on this server:**", $"```css\n• " + string.Join("\n• ", roles).SanitizeMentions() + "\n```").ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -175,19 +196,23 @@ namespace NadekoBot.Modules.Utility
|
|||||||
var stats = NadekoBot.Stats;
|
var stats = NadekoBot.Stats;
|
||||||
|
|
||||||
await channel.EmbedAsync(
|
await channel.EmbedAsync(
|
||||||
new EmbedBuilder().WithColor(new Color(0x00bbd6))
|
new EmbedBuilder().WithOkColor()
|
||||||
.WithAuthor(eab => eab.WithName($"NadekoBot v{StatsService.BotVersion}")
|
.WithAuthor(eab => eab.WithName($"NadekoBot v{StatsService.BotVersion}")
|
||||||
.WithUrl("http://nadekobot.readthedocs.io/en/latest/")
|
.WithUrl("http://nadekobot.readthedocs.io/en/latest/")
|
||||||
.WithIconUrl("https://cdn.discordapp.com/avatars/116275390695079945/b21045e778ef21c96d175400e779f0fb.jpg"))
|
.WithIconUrl("https://cdn.discordapp.com/avatars/116275390695079945/b21045e778ef21c96d175400e779f0fb.jpg"))
|
||||||
.AddField(efb => efb.WithName(Format.Bold("Author")).WithValue(stats.Author).WithIsInline(true))
|
.AddField(efb => efb.WithName(Format.Bold("Author")).WithValue(stats.Author).WithIsInline(true))
|
||||||
.AddField(efb => efb.WithName(Format.Bold("Library")).WithValue(stats.Library).WithIsInline(true))
|
.AddField(efb => efb.WithName(Format.Bold("Library")).WithValue(stats.Library).WithIsInline(true))
|
||||||
.AddField(efb => efb.WithName(Format.Bold("Bot ID")).WithValue(NadekoBot.Client.CurrentUser().Id.ToString()).WithIsInline(true))
|
.AddField(efb => efb.WithName(Format.Bold("Bot ID")).WithValue(NadekoBot.Client.GetCurrentUser().Id.ToString()).WithIsInline(true))
|
||||||
.AddField(efb => efb.WithName(Format.Bold("Commands Ran")).WithValue(stats.CommandsRan.ToString()).WithIsInline(true))
|
.AddField(efb => efb.WithName(Format.Bold("Commands Ran")).WithValue(stats.CommandsRan.ToString()).WithIsInline(true))
|
||||||
.AddField(efb => efb.WithName(Format.Bold("Messages")).WithValue($"{stats.MessageCounter} ({stats.MessagesPerSecond:F2}/sec)").WithIsInline(true))
|
.AddField(efb => efb.WithName(Format.Bold("Messages")).WithValue($"{stats.MessageCounter} ({stats.MessagesPerSecond:F2}/sec)").WithIsInline(true))
|
||||||
.AddField(efb => efb.WithName(Format.Bold("Memory")).WithValue($"{stats.Heap} MB").WithIsInline(true))
|
.AddField(efb => efb.WithName(Format.Bold("Memory")).WithValue($"{stats.Heap} MB").WithIsInline(true))
|
||||||
.AddField(efb => efb.WithName(Format.Bold("Owner ID(s)")).WithValue(stats.OwnerIds).WithIsInline(true))
|
.AddField(efb => efb.WithName(Format.Bold("Owner ID(s)")).WithValue(stats.OwnerIds).WithIsInline(true))
|
||||||
.AddField(efb => efb.WithName(Format.Bold("Uptime")).WithValue(stats.GetUptimeString("\n")).WithIsInline(true))
|
.AddField(efb => efb.WithName(Format.Bold("Uptime")).WithValue(stats.GetUptimeString("\n")).WithIsInline(true))
|
||||||
.AddField(efb => efb.WithName(Format.Bold("Presence")).WithValue($"{NadekoBot.Client.GetGuilds().Count} Servers\n{stats.TextChannels} Text Channels\n{stats.VoiceChannels} Voice Channels").WithIsInline(true)));
|
.AddField(efb => efb.WithName(Format.Bold("Presence")).WithValue($"{NadekoBot.Client.GetGuilds().Count} Servers\n{stats.TextChannels} Text Channels\n{stats.VoiceChannels} Voice Channels").WithIsInline(true))
|
||||||
|
#if !GLOBAL_NADEKO
|
||||||
|
.WithFooter(efb => efb.WithText($"Playing {Music.Music.MusicPlayers.Where(mp => mp.Value.CurrentSong != null).Count()} songs, {Music.Music.MusicPlayers.Sum(mp => mp.Value.Playlist.Count)} queued."))
|
||||||
|
#endif
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Regex emojiFinder { get; } = new Regex(@"<:(?<name>.+?):(?<id>\d*)>", RegexOptions.Compiled);
|
private Regex emojiFinder { get; } = new Regex(@"<:(?<name>.+?):(?<id>\d*)>", RegexOptions.Compiled);
|
||||||
@ -225,7 +250,7 @@ namespace NadekoBot.Modules.Utility
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await channel.EmbedAsync(guilds.Aggregate(new EmbedBuilder().WithColor(NadekoBot.OkColor),
|
await channel.EmbedAsync(guilds.Aggregate(new EmbedBuilder().WithOkColor(),
|
||||||
(embed, g) => embed.AddField(efb => efb.WithName(g.Name)
|
(embed, g) => embed.AddField(efb => efb.WithName(g.Name)
|
||||||
.WithValue($"```css\nID: {g.Id}\nMembers: {g.Users.Count}\nOwnerID: {g.OwnerId} ```")
|
.WithValue($"```css\nID: {g.Id}\nMembers: {g.Users.Count}\nOwnerID: {g.OwnerId} ```")
|
||||||
.WithIsInline(false))))
|
.WithIsInline(false))))
|
||||||
|
@ -89,7 +89,7 @@ namespace NadekoBot
|
|||||||
//connect
|
//connect
|
||||||
await Client.LoginAsync(TokenType.Bot, Credentials.Token).ConfigureAwait(false);
|
await Client.LoginAsync(TokenType.Bot, Credentials.Token).ConfigureAwait(false);
|
||||||
await Client.ConnectAsync().ConfigureAwait(false);
|
await Client.ConnectAsync().ConfigureAwait(false);
|
||||||
await Client.DownloadAllUsersAsync().ConfigureAwait(false);
|
//await Client.DownloadAllUsersAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
_log.Info("Connected");
|
_log.Info("Connected");
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<DnxInvisibleContent Include="data\questions.json" />
|
<DnxInvisibleContent Include="data\questions.json" />
|
||||||
|
<DnxInvisibleContent Include="data\questions2.json" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||||
</Project>
|
</Project>
|
267
src/NadekoBot/Resources/CommandStrings.Designer.cs
generated
267
src/NadekoBot/Resources/CommandStrings.Designer.cs
generated
@ -86,6 +86,60 @@ namespace NadekoBot.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to acrophobia acro.
|
||||||
|
/// </summary>
|
||||||
|
public static string acro_cmd {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("acro_cmd", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Starts an Acrophobia game. Second argment is optional round length in seconds. (default is 60).
|
||||||
|
/// </summary>
|
||||||
|
public static string acro_desc {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("acro_desc", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to `{0}acro` or `{0}acro 30`.
|
||||||
|
/// </summary>
|
||||||
|
public static string acro_usage {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("acro_usage", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to activity.
|
||||||
|
/// </summary>
|
||||||
|
public static string activity_cmd {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("activity_cmd", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Checks for spammers..
|
||||||
|
/// </summary>
|
||||||
|
public static string activity_desc {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("activity_desc", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to `{0}activity`.
|
||||||
|
/// </summary>
|
||||||
|
public static string activity_usage {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("activity_usage", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to addcustreact acr.
|
/// Looks up a localized string similar to addcustreact acr.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -545,6 +599,33 @@ namespace NadekoBot.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to autohentai.
|
||||||
|
/// </summary>
|
||||||
|
public static string autohentai_cmd {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("autohentai_cmd", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Posts a hentai every X seconds with a random tag from the provided tags. Use `|` to separate tags. 20 seconds minimum. Provide no arguments to disable..
|
||||||
|
/// </summary>
|
||||||
|
public static string autohentai_desc {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("autohentai_desc", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to `{0}autohentai 30 yuri|tail|long_hair` or `{0}autohentai`.
|
||||||
|
/// </summary>
|
||||||
|
public static string autohentai_usage {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("autohentai_usage", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to autoplay ap.
|
/// Looks up a localized string similar to autoplay ap.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -1347,7 +1428,7 @@ namespace NadekoBot.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to `{0}cm "module name" enable SomeChannel`.
|
/// Looks up a localized string similar to `{0}cm ModuleName enable SomeChannel`.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string chnlmdl_usage {
|
public static string chnlmdl_usage {
|
||||||
get {
|
get {
|
||||||
@ -1976,6 +2057,33 @@ namespace NadekoBot.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to define def.
|
||||||
|
/// </summary>
|
||||||
|
public static string define_cmd {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("define_cmd", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Finds a definition of a word..
|
||||||
|
/// </summary>
|
||||||
|
public static string define_desc {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("define_desc", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to `{0}def heresy`.
|
||||||
|
/// </summary>
|
||||||
|
public static string define_usage {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("define_usage", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to defvol dv.
|
/// Looks up a localized string similar to defvol dv.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -2408,6 +2516,33 @@ namespace NadekoBot.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to fairplay fp.
|
||||||
|
/// </summary>
|
||||||
|
public static string fairplay_cmd {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("fairplay_cmd", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Toggles fairplay. While enabled, music player will prioritize songs from users who didn't have their song recently played instead of the song's position in the queue..
|
||||||
|
/// </summary>
|
||||||
|
public static string fairplay_desc {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("fairplay_desc", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to `{0}fp`.
|
||||||
|
/// </summary>
|
||||||
|
public static string fairplay_usage {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("fairplay_usage", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to fw.
|
/// Looks up a localized string similar to fw.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -2570,33 +2705,6 @@ namespace NadekoBot.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Looks up a localized string similar to getlink gl.
|
|
||||||
/// </summary>
|
|
||||||
public static string getlink_cmd {
|
|
||||||
get {
|
|
||||||
return ResourceManager.GetString("getlink_cmd", resourceCulture);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Looks up a localized string similar to Shows a link to the song in the queue by index, or the currently playing song by default..
|
|
||||||
/// </summary>
|
|
||||||
public static string getlink_desc {
|
|
||||||
get {
|
|
||||||
return ResourceManager.GetString("getlink_desc", resourceCulture);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Looks up a localized string similar to `{0}gl`.
|
|
||||||
/// </summary>
|
|
||||||
public static string getlink_usage {
|
|
||||||
get {
|
|
||||||
return ResourceManager.GetString("getlink_usage", resourceCulture);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to give.
|
/// Looks up a localized string similar to give.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -3893,6 +4001,60 @@ namespace NadekoBot.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to log.
|
||||||
|
/// </summary>
|
||||||
|
public static string log_cmd {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("log_cmd", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Toggles logging event. Disables it if it's active anywhere on the server. Enables if it's not active. Use `{0}logevents` to see a list of all events you can subscribe to..
|
||||||
|
/// </summary>
|
||||||
|
public static string log_desc {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("log_desc", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to `{0}log userpresence` or `{0}log userbanned`.
|
||||||
|
/// </summary>
|
||||||
|
public static string log_usage {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("log_usage", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to logevents.
|
||||||
|
/// </summary>
|
||||||
|
public static string logevents_cmd {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("logevents_cmd", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Shows a list of all events you can subscribe to with `{0}log`.
|
||||||
|
/// </summary>
|
||||||
|
public static string logevents_desc {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("logevents_desc", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to `{0}logevents`.
|
||||||
|
/// </summary>
|
||||||
|
public static string logevents_usage {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("logevents_usage", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to logignore.
|
/// Looks up a localized string similar to logignore.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -3930,7 +4092,7 @@ namespace NadekoBot.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Logs server activity in this channel..
|
/// Looks up a localized string similar to Enables or Disables ALL log events. If enabled, all log events will log to this channel..
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string logserver_desc {
|
public static string logserver_desc {
|
||||||
get {
|
get {
|
||||||
@ -3939,7 +4101,7 @@ namespace NadekoBot.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to `{0}logserver`.
|
/// Looks up a localized string similar to `{0}logserver enable` or `{0}logserver disable`.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string logserver_usage {
|
public static string logserver_usage {
|
||||||
get {
|
get {
|
||||||
@ -4649,7 +4811,7 @@ namespace NadekoBot.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to overwatch ow.
|
/// Looks up a localized string similar to overwatch ow.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string overwatch_cmd {
|
public static string overwatch_cmd {
|
||||||
@ -4740,7 +4902,7 @@ namespace NadekoBot.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Picks the currency planted in this channel..
|
/// Looks up a localized string similar to Picks the currency planted in this channel. 60 seconds cooldown..
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string pick_desc {
|
public static string pick_desc {
|
||||||
get {
|
get {
|
||||||
@ -5775,7 +5937,7 @@ namespace NadekoBot.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to `{0}rm "module name" enable MyRole`.
|
/// Looks up a localized string similar to `{0}rm ModuleName enable MyRole`.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string rolemdl_usage {
|
public static string rolemdl_usage {
|
||||||
get {
|
get {
|
||||||
@ -5820,7 +5982,7 @@ namespace NadekoBot.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Rolls 0-100. If you supply a number [x] it rolls up to 30 normal dice. If you split 2 numbers with letter d (xdy) it will roll x dice from 1 to y..
|
/// Looks up a localized string similar to Rolls 0-100. If you supply a number [x] it rolls up to 30 normal dice. If you split 2 numbers with letter d (xdy) it will roll x dice from 1 to y. Y can be a letter 'F' if you want to roll fate dice instead of dnd..
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string roll_desc {
|
public static string roll_desc {
|
||||||
get {
|
get {
|
||||||
@ -5829,7 +5991,7 @@ namespace NadekoBot.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to `{0}roll` or `{0}roll 7` or `{0}roll 3d5`.
|
/// Looks up a localized string similar to `{0}roll` or `{0}roll 7` or `{0}roll 3d5` or `{0}roll 5dF`.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string roll_usage {
|
public static string roll_usage {
|
||||||
get {
|
get {
|
||||||
@ -6269,6 +6431,33 @@ namespace NadekoBot.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to setmaxplaytime smp.
|
||||||
|
/// </summary>
|
||||||
|
public static string setmaxplaytime_cmd {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("setmaxplaytime_cmd", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Sets a maximum number of seconds (>14) a song can run before being skipped automatically. Set 0 to have no limit..
|
||||||
|
/// </summary>
|
||||||
|
public static string setmaxplaytime_desc {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("setmaxplaytime_desc", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to `{0}smp 0` or `{0}smp 270`.
|
||||||
|
/// </summary>
|
||||||
|
public static string setmaxplaytime_usage {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("setmaxplaytime_usage", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to setmaxqueue smq.
|
/// Looks up a localized string similar to setmaxqueue smq.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -6828,7 +7017,7 @@ namespace NadekoBot.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to `{0}sm "module name" enable`.
|
/// Looks up a localized string similar to `{0}sm ModuleName enable`.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string srvrmdl_usage {
|
public static string srvrmdl_usage {
|
||||||
get {
|
get {
|
||||||
@ -7611,7 +7800,7 @@ namespace NadekoBot.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to `{0}um "module name" enable SomeUsername`.
|
/// Looks up a localized string similar to `{0}um ModuleName enable SomeUsername`.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string usrmdl_usage {
|
public static string usrmdl_usage {
|
||||||
get {
|
get {
|
||||||
@ -7818,7 +8007,7 @@ namespace NadekoBot.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Shows weather data for a specified city and a country. BOTH ARE REQUIRED. Use country abbrevations..
|
/// Looks up a localized string similar to Shows weather data for a specified city. You can also specify a country after a comma..
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string weather_desc {
|
public static string weather_desc {
|
||||||
get {
|
get {
|
||||||
@ -7827,7 +8016,7 @@ namespace NadekoBot.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to `{0}we Moscow RF`.
|
/// Looks up a localized string similar to `{0}we Moscow, RU`.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string weather_usage {
|
public static string weather_usage {
|
||||||
get {
|
get {
|
||||||
|
@ -229,10 +229,10 @@
|
|||||||
<value>logserver</value>
|
<value>logserver</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="logserver_desc" xml:space="preserve">
|
<data name="logserver_desc" xml:space="preserve">
|
||||||
<value>Logs server activity in this channel.</value>
|
<value>Enables or Disables ALL log events. If enabled, all log events will log to this channel.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="logserver_usage" xml:space="preserve">
|
<data name="logserver_usage" xml:space="preserve">
|
||||||
<value>`{0}logserver`</value>
|
<value>`{0}logserver enable` or `{0}logserver disable`</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="logignore_cmd" xml:space="preserve">
|
<data name="logignore_cmd" xml:space="preserve">
|
||||||
<value>logignore</value>
|
<value>logignore</value>
|
||||||
@ -988,7 +988,7 @@
|
|||||||
<value>Sets a module's permission at the server level.</value>
|
<value>Sets a module's permission at the server level.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="srvrmdl_usage" xml:space="preserve">
|
<data name="srvrmdl_usage" xml:space="preserve">
|
||||||
<value>`{0}sm "module name" enable`</value>
|
<value>`{0}sm ModuleName enable`</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="srvrcmd_cmd" xml:space="preserve">
|
<data name="srvrcmd_cmd" xml:space="preserve">
|
||||||
<value>srvrcmd sc</value>
|
<value>srvrcmd sc</value>
|
||||||
@ -1006,7 +1006,7 @@
|
|||||||
<value>Sets a module's permission at the role level.</value>
|
<value>Sets a module's permission at the role level.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="rolemdl_usage" xml:space="preserve">
|
<data name="rolemdl_usage" xml:space="preserve">
|
||||||
<value>`{0}rm "module name" enable MyRole`</value>
|
<value>`{0}rm ModuleName enable MyRole`</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="rolecmd_cmd" xml:space="preserve">
|
<data name="rolecmd_cmd" xml:space="preserve">
|
||||||
<value>rolecmd rc</value>
|
<value>rolecmd rc</value>
|
||||||
@ -1024,7 +1024,7 @@
|
|||||||
<value>Sets a module's permission at the channel level.</value>
|
<value>Sets a module's permission at the channel level.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="chnlmdl_usage" xml:space="preserve">
|
<data name="chnlmdl_usage" xml:space="preserve">
|
||||||
<value>`{0}cm "module name" enable SomeChannel`</value>
|
<value>`{0}cm ModuleName enable SomeChannel`</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="chnlcmd_cmd" xml:space="preserve">
|
<data name="chnlcmd_cmd" xml:space="preserve">
|
||||||
<value>chnlcmd cc</value>
|
<value>chnlcmd cc</value>
|
||||||
@ -1042,7 +1042,7 @@
|
|||||||
<value>Sets a module's permission at the user level.</value>
|
<value>Sets a module's permission at the user level.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="usrmdl_usage" xml:space="preserve">
|
<data name="usrmdl_usage" xml:space="preserve">
|
||||||
<value>`{0}um "module name" enable SomeUsername`</value>
|
<value>`{0}um ModuleName enable SomeUsername`</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="usrcmd_cmd" xml:space="preserve">
|
<data name="usrcmd_cmd" xml:space="preserve">
|
||||||
<value>usrcmd uc</value>
|
<value>usrcmd uc</value>
|
||||||
@ -1192,10 +1192,10 @@
|
|||||||
<value>roll</value>
|
<value>roll</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="roll_desc" xml:space="preserve">
|
<data name="roll_desc" xml:space="preserve">
|
||||||
<value>Rolls 0-100. If you supply a number [x] it rolls up to 30 normal dice. If you split 2 numbers with letter d (xdy) it will roll x dice from 1 to y.</value>
|
<value>Rolls 0-100. If you supply a number [x] it rolls up to 30 normal dice. If you split 2 numbers with letter d (xdy) it will roll x dice from 1 to y. Y can be a letter 'F' if you want to roll fate dice instead of dnd.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="roll_usage" xml:space="preserve">
|
<data name="roll_usage" xml:space="preserve">
|
||||||
<value>`{0}roll` or `{0}roll 7` or `{0}roll 3d5`</value>
|
<value>`{0}roll` or `{0}roll 7` or `{0}roll 3d5` or `{0}roll 5dF`</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="rolluo_cmd" xml:space="preserve">
|
<data name="rolluo_cmd" xml:space="preserve">
|
||||||
<value>rolluo</value>
|
<value>rolluo</value>
|
||||||
@ -1363,7 +1363,7 @@
|
|||||||
<value>pick</value>
|
<value>pick</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="pick_desc" xml:space="preserve">
|
<data name="pick_desc" xml:space="preserve">
|
||||||
<value>Picks the currency planted in this channel.</value>
|
<value>Picks the currency planted in this channel. 60 seconds cooldown.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="pick_usage" xml:space="preserve">
|
<data name="pick_usage" xml:space="preserve">
|
||||||
<value>`{0}pick`</value>
|
<value>`{0}pick`</value>
|
||||||
@ -1692,15 +1692,6 @@
|
|||||||
<data name="goto_usage" xml:space="preserve">
|
<data name="goto_usage" xml:space="preserve">
|
||||||
<value>`{0}goto 30`</value>
|
<value>`{0}goto 30`</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="getlink_cmd" xml:space="preserve">
|
|
||||||
<value>getlink gl</value>
|
|
||||||
</data>
|
|
||||||
<data name="getlink_desc" xml:space="preserve">
|
|
||||||
<value>Shows a link to the song in the queue by index, or the currently playing song by default.</value>
|
|
||||||
</data>
|
|
||||||
<data name="getlink_usage" xml:space="preserve">
|
|
||||||
<value>`{0}gl`</value>
|
|
||||||
</data>
|
|
||||||
<data name="autoplay_cmd" xml:space="preserve">
|
<data name="autoplay_cmd" xml:space="preserve">
|
||||||
<value>autoplay ap</value>
|
<value>autoplay ap</value>
|
||||||
</data>
|
</data>
|
||||||
@ -1876,10 +1867,10 @@
|
|||||||
<value>weather we</value>
|
<value>weather we</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="weather_desc" xml:space="preserve">
|
<data name="weather_desc" xml:space="preserve">
|
||||||
<value>Shows weather data for a specified city and a country. BOTH ARE REQUIRED. Use country abbrevations.</value>
|
<value>Shows weather data for a specified city. You can also specify a country after a comma.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="weather_usage" xml:space="preserve">
|
<data name="weather_usage" xml:space="preserve">
|
||||||
<value>`{0}we Moscow RF`</value>
|
<value>`{0}we Moscow, RU`</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="youtube_cmd" xml:space="preserve">
|
<data name="youtube_cmd" xml:space="preserve">
|
||||||
<value>youtube yt</value>
|
<value>youtube yt</value>
|
||||||
@ -2781,7 +2772,7 @@
|
|||||||
<data name="crstats_usage" xml:space="preserve">
|
<data name="crstats_usage" xml:space="preserve">
|
||||||
<value>`{0}crstats` or `{0}crstats 3`</value>
|
<value>`{0}crstats` or `{0}crstats 3`</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="overwatch_cmd" xml:space="preserve">
|
<data name="overwatch_cmd" xml:space="preserve">
|
||||||
<value>overwatch ow</value>
|
<value>overwatch ow</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="overwatch_desc" xml:space="preserve">
|
<data name="overwatch_desc" xml:space="preserve">
|
||||||
@ -2790,4 +2781,76 @@
|
|||||||
<data name="overwatch_usage" xml:space="preserve">
|
<data name="overwatch_usage" xml:space="preserve">
|
||||||
<value>`{0}ow us Battletag#1337` or `{0}overwatch eu Battletag#2016`</value>
|
<value>`{0}ow us Battletag#1337` or `{0}overwatch eu Battletag#2016`</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="acro_cmd" xml:space="preserve">
|
||||||
|
<value>acrophobia acro</value>
|
||||||
|
</data>
|
||||||
|
<data name="acro_desc" xml:space="preserve">
|
||||||
|
<value>Starts an Acrophobia game. Second argment is optional round length in seconds. (default is 60)</value>
|
||||||
|
</data>
|
||||||
|
<data name="acro_usage" xml:space="preserve">
|
||||||
|
<value>`{0}acro` or `{0}acro 30`</value>
|
||||||
|
</data>
|
||||||
|
<data name="logevents_cmd" xml:space="preserve">
|
||||||
|
<value>logevents</value>
|
||||||
|
</data>
|
||||||
|
<data name="logevents_desc" xml:space="preserve">
|
||||||
|
<value>Shows a list of all events you can subscribe to with `{0}log`</value>
|
||||||
|
</data>
|
||||||
|
<data name="logevents_usage" xml:space="preserve">
|
||||||
|
<value>`{0}logevents`</value>
|
||||||
|
</data>
|
||||||
|
<data name="log_cmd" xml:space="preserve">
|
||||||
|
<value>log</value>
|
||||||
|
</data>
|
||||||
|
<data name="log_desc" xml:space="preserve">
|
||||||
|
<value>Toggles logging event. Disables it if it's active anywhere on the server. Enables if it's not active. Use `{0}logevents` to see a list of all events you can subscribe to.</value>
|
||||||
|
</data>
|
||||||
|
<data name="log_usage" xml:space="preserve">
|
||||||
|
<value>`{0}log userpresence` or `{0}log userbanned`</value>
|
||||||
|
</data>
|
||||||
|
<data name="fairplay_cmd" xml:space="preserve">
|
||||||
|
<value>fairplay fp</value>
|
||||||
|
</data>
|
||||||
|
<data name="fairplay_desc" xml:space="preserve">
|
||||||
|
<value>Toggles fairplay. While enabled, music player will prioritize songs from users who didn't have their song recently played instead of the song's position in the queue.</value>
|
||||||
|
</data>
|
||||||
|
<data name="fairplay_usage" xml:space="preserve">
|
||||||
|
<value>`{0}fp`</value>
|
||||||
|
</data>
|
||||||
|
<data name="define_cmd" xml:space="preserve">
|
||||||
|
<value>define def</value>
|
||||||
|
</data>
|
||||||
|
<data name="define_desc" xml:space="preserve">
|
||||||
|
<value>Finds a definition of a word.</value>
|
||||||
|
</data>
|
||||||
|
<data name="define_usage" xml:space="preserve">
|
||||||
|
<value>`{0}def heresy`</value>
|
||||||
|
</data>
|
||||||
|
<data name="setmaxplaytime_cmd" xml:space="preserve">
|
||||||
|
<value>setmaxplaytime smp</value>
|
||||||
|
</data>
|
||||||
|
<data name="setmaxplaytime_desc" xml:space="preserve">
|
||||||
|
<value>Sets a maximum number of seconds (>14) a song can run before being skipped automatically. Set 0 to have no limit.</value>
|
||||||
|
</data>
|
||||||
|
<data name="setmaxplaytime_usage" xml:space="preserve">
|
||||||
|
<value>`{0}smp 0` or `{0}smp 270`</value>
|
||||||
|
</data>
|
||||||
|
<data name="activity_cmd" xml:space="preserve">
|
||||||
|
<value>activity</value>
|
||||||
|
</data>
|
||||||
|
<data name="activity_desc" xml:space="preserve">
|
||||||
|
<value>Checks for spammers.</value>
|
||||||
|
</data>
|
||||||
|
<data name="activity_usage" xml:space="preserve">
|
||||||
|
<value>`{0}activity`</value>
|
||||||
|
</data>
|
||||||
|
<data name="autohentai_cmd" xml:space="preserve">
|
||||||
|
<value>autohentai</value>
|
||||||
|
</data>
|
||||||
|
<data name="autohentai_desc" xml:space="preserve">
|
||||||
|
<value>Posts a hentai every X seconds with a random tag from the provided tags. Use `|` to separate tags. 20 seconds minimum. Provide no arguments to disable.</value>
|
||||||
|
</data>
|
||||||
|
<data name="autohentai_usage" xml:space="preserve">
|
||||||
|
<value>`{0}autohentai 30 yuri|tail|long_hair` or `{0}autohentai`</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -2,33 +2,104 @@
|
|||||||
|
|
||||||
namespace NadekoBot.Services.Database.Models
|
namespace NadekoBot.Services.Database.Models
|
||||||
{
|
{
|
||||||
|
|
||||||
public class LogSetting : DbEntity
|
public class LogSetting : DbEntity
|
||||||
{
|
{
|
||||||
public bool IsLogging { get; set; }
|
public HashSet<IgnoredLogChannel> IgnoredChannels { get; set; } = new HashSet<IgnoredLogChannel>();
|
||||||
public ulong ChannelId { get; set; }
|
public HashSet<IgnoredVoicePresenceChannel> IgnoredVoicePresenceChannelIds { get; set; } = new HashSet<IgnoredVoicePresenceChannel>();
|
||||||
public HashSet<IgnoredLogChannel> IgnoredChannels { get; set; }
|
|
||||||
|
|
||||||
public bool MessageUpdated { get; set; } = true;
|
public ulong? LogOtherId { get; set; } = null;
|
||||||
public bool MessageDeleted { get; set; } = true;
|
public ulong? MessageUpdatedId { get; set; } = null;
|
||||||
|
public ulong? MessageDeletedId { get; set; } = null;
|
||||||
|
|
||||||
public bool UserJoined { get; set; } = true;
|
public ulong? UserJoinedId { get; set; } = null;
|
||||||
public bool UserLeft { get; set; } = true;
|
public ulong? UserLeftId { get; set; } = null;
|
||||||
public bool UserBanned { get; set; } = true;
|
public ulong? UserBannedId { get; set; } = null;
|
||||||
public bool UserUnbanned { get; set; } = true;
|
public ulong? UserUnbannedId { get; set; } = null;
|
||||||
public bool UserUpdated { get; set; } = true;
|
public ulong? UserUpdatedId { get; set; } = null;
|
||||||
|
|
||||||
public bool ChannelCreated { get; set; } = true;
|
public ulong? ChannelCreatedId { get; set; } = null;
|
||||||
public bool ChannelDestroyed { get; set; } = true;
|
public ulong? ChannelDestroyedId { get; set; } = null;
|
||||||
public bool ChannelUpdated { get; set; } = true;
|
public ulong? ChannelUpdatedId { get; set; } = null;
|
||||||
|
|
||||||
|
public ulong? UserMutedId { get; set; }
|
||||||
|
|
||||||
//userpresence
|
//userpresence
|
||||||
public bool LogUserPresence { get; set; } = false;
|
public ulong? LogUserPresenceId { get; set; } = null;
|
||||||
public ulong UserPresenceChannelId { get; set; }
|
|
||||||
|
|
||||||
//voicepresence
|
//voicepresence
|
||||||
public bool LogVoicePresence { get; set; } = false;
|
|
||||||
public ulong VoicePresenceChannelId { get; set; }
|
|
||||||
public HashSet<IgnoredVoicePresenceChannel> IgnoredVoicePresenceChannelIds { get; set; }
|
|
||||||
|
|
||||||
|
public ulong? LogVoicePresenceId { get; set; } = null;
|
||||||
|
public ulong? LogVoicePresenceTTSId { get; set; } = null;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//-------------------DO NOT USE----------------
|
||||||
|
// these old fields are here because sqlite doesn't support drop column operation
|
||||||
|
// will be removed after bot moves to another database provider
|
||||||
|
/// <summary>
|
||||||
|
/// DON'T USE
|
||||||
|
/// </summary>
|
||||||
|
public bool IsLogging { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// DON'T USE
|
||||||
|
/// </summary>
|
||||||
|
public ulong ChannelId { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// DON'T USE
|
||||||
|
/// </summary>
|
||||||
|
public bool MessageUpdated { get; set; } = true;
|
||||||
|
/// <summary>
|
||||||
|
/// DON'T USE
|
||||||
|
/// </summary>
|
||||||
|
public bool MessageDeleted { get; set; } = true;
|
||||||
|
/// <summary>
|
||||||
|
/// DON'T USE
|
||||||
|
/// </summary>
|
||||||
|
public bool UserJoined { get; set; } = true;
|
||||||
|
/// <summary>
|
||||||
|
/// DON'T USE
|
||||||
|
/// </summary>
|
||||||
|
public bool UserLeft { get; set; } = true;
|
||||||
|
/// <summary>
|
||||||
|
/// DON'T USE
|
||||||
|
/// </summary>
|
||||||
|
public bool UserBanned { get; set; } = true;
|
||||||
|
/// <summary>
|
||||||
|
/// DON'T USE
|
||||||
|
/// </summary>
|
||||||
|
public bool UserUnbanned { get; set; } = true;
|
||||||
|
/// <summary>
|
||||||
|
/// DON'T USE
|
||||||
|
/// </summary>
|
||||||
|
public bool UserUpdated { get; set; } = true;
|
||||||
|
/// <summary>
|
||||||
|
/// DON'T USE
|
||||||
|
/// </summary>
|
||||||
|
public bool ChannelCreated { get; set; } = true;
|
||||||
|
/// <summary>
|
||||||
|
/// DON'T USE
|
||||||
|
/// </summary>
|
||||||
|
public bool ChannelDestroyed { get; set; } = true;
|
||||||
|
/// <summary>
|
||||||
|
/// DON'T USE
|
||||||
|
/// </summary>
|
||||||
|
public bool ChannelUpdated { get; set; } = true;
|
||||||
|
/// <summary>
|
||||||
|
/// DON'T USE
|
||||||
|
/// </summary>
|
||||||
|
public bool LogUserPresence { get; set; } = false;
|
||||||
|
/// <summary>
|
||||||
|
/// DON'T USE
|
||||||
|
/// </summary>
|
||||||
|
public ulong UserPresenceChannelId { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// DON'T USE
|
||||||
|
/// </summary>
|
||||||
|
public bool LogVoicePresence { get; set; } = false;
|
||||||
|
/// <summary>
|
||||||
|
/// DON'T USE
|
||||||
|
/// </summary>
|
||||||
|
public ulong VoicePresenceChannelId { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -9,6 +9,7 @@ namespace NadekoBot.Services.Database.Repositories
|
|||||||
public interface IGuildConfigRepository : IRepository<GuildConfig>
|
public interface IGuildConfigRepository : IRepository<GuildConfig>
|
||||||
{
|
{
|
||||||
GuildConfig For(ulong guildId, Func<DbSet<GuildConfig>, IQueryable<GuildConfig>> includes = null);
|
GuildConfig For(ulong guildId, Func<DbSet<GuildConfig>, IQueryable<GuildConfig>> includes = null);
|
||||||
|
GuildConfig LogSettingsFor(ulong guildId);
|
||||||
GuildConfig PermissionsFor(ulong guildId);
|
GuildConfig PermissionsFor(ulong guildId);
|
||||||
IEnumerable<GuildConfig> PermissionsForAll();
|
IEnumerable<GuildConfig> PermissionsForAll();
|
||||||
IEnumerable<GuildConfig> GetAllGuildConfigs();
|
IEnumerable<GuildConfig> GetAllGuildConfigs();
|
||||||
|
@ -16,8 +16,6 @@ namespace NadekoBot.Services.Database.Repositories.Impl
|
|||||||
public IEnumerable<GuildConfig> GetAllGuildConfigs() =>
|
public IEnumerable<GuildConfig> GetAllGuildConfigs() =>
|
||||||
_set.Include(gc => gc.LogSetting)
|
_set.Include(gc => gc.LogSetting)
|
||||||
.ThenInclude(ls => ls.IgnoredChannels)
|
.ThenInclude(ls => ls.IgnoredChannels)
|
||||||
.Include(gc => gc.LogSetting)
|
|
||||||
.ThenInclude(ls => ls.IgnoredVoicePresenceChannelIds)
|
|
||||||
.Include(gc => gc.RootPermission)
|
.Include(gc => gc.RootPermission)
|
||||||
.ThenInclude(gc => gc.Previous)
|
.ThenInclude(gc => gc.Previous)
|
||||||
.Include(gc => gc.RootPermission)
|
.Include(gc => gc.RootPermission)
|
||||||
@ -43,10 +41,8 @@ namespace NadekoBot.Services.Database.Repositories.Impl
|
|||||||
{
|
{
|
||||||
config = _set
|
config = _set
|
||||||
.Include(gc => gc.FollowedStreams)
|
.Include(gc => gc.FollowedStreams)
|
||||||
.Include(gc => gc.LogSetting)
|
|
||||||
.ThenInclude(ls => ls.IgnoredChannels)
|
|
||||||
.Include(gc => gc.LogSetting)
|
.Include(gc => gc.LogSetting)
|
||||||
.ThenInclude(ls => ls.IgnoredVoicePresenceChannelIds)
|
.ThenInclude(ls => ls.IgnoredChannels)
|
||||||
.Include(gc => gc.FilterInvitesChannelIds)
|
.Include(gc => gc.FilterInvitesChannelIds)
|
||||||
.Include(gc => gc.FilterWordsChannelIds)
|
.Include(gc => gc.FilterWordsChannelIds)
|
||||||
.Include(gc => gc.FilteredWords)
|
.Include(gc => gc.FilteredWords)
|
||||||
@ -72,6 +68,13 @@ namespace NadekoBot.Services.Database.Repositories.Impl
|
|||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public GuildConfig LogSettingsFor(ulong guildId)
|
||||||
|
{
|
||||||
|
return _set.Include(gc => gc.LogSetting)
|
||||||
|
.ThenInclude(gc => gc.IgnoredChannels)
|
||||||
|
.FirstOrDefault();
|
||||||
|
}
|
||||||
|
|
||||||
public GuildConfig PermissionsFor(ulong guildId)
|
public GuildConfig PermissionsFor(ulong guildId)
|
||||||
{
|
{
|
||||||
var query = _set.Include(gc => gc.RootPermission);
|
var query = _set.Include(gc => gc.RootPermission);
|
||||||
|
@ -14,7 +14,7 @@ namespace NadekoBot.Services.Impl
|
|||||||
private ShardedDiscordClient client;
|
private ShardedDiscordClient client;
|
||||||
private DateTime started;
|
private DateTime started;
|
||||||
|
|
||||||
public const string BotVersion = "1.0-rc3";
|
public const string BotVersion = "1.1.0-alpha";
|
||||||
|
|
||||||
public string Author => "Kwoth#2560";
|
public string Author => "Kwoth#2560";
|
||||||
public string Library => "Discord.Net";
|
public string Library => "Discord.Net";
|
||||||
|
@ -5,6 +5,7 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using NLog;
|
using NLog;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
namespace NadekoBot
|
namespace NadekoBot
|
||||||
{
|
{
|
||||||
@ -13,24 +14,27 @@ namespace NadekoBot
|
|||||||
private DiscordSocketConfig discordSocketConfig;
|
private DiscordSocketConfig discordSocketConfig;
|
||||||
private Logger _log { get; }
|
private Logger _log { get; }
|
||||||
|
|
||||||
public event Func<SocketGuildUser, Task> UserJoined = delegate { return Task.CompletedTask; };
|
public event Action<IGuildUser> UserJoined = delegate { };
|
||||||
public event Func<SocketMessage, Task> MessageReceived = delegate { return Task.CompletedTask; };
|
public event Action<IMessage> MessageReceived = delegate { };
|
||||||
public event Func<SocketGuildUser, Task> UserLeft = delegate { return Task.CompletedTask; };
|
public event Action<IGuildUser> UserLeft = delegate { };
|
||||||
public event Func<SocketUser, SocketUser, Task> UserUpdated = delegate { return Task.CompletedTask; };
|
public event Action<IGuildUser, IGuildUser> UserUpdated = delegate { };
|
||||||
public event Func<Optional<SocketMessage>, SocketMessage, Task> MessageUpdated = delegate { return Task.CompletedTask; };
|
public event Action<Optional<IMessage>, IMessage> MessageUpdated = delegate { };
|
||||||
public event Func<ulong, Optional<SocketMessage>, Task> MessageDeleted = delegate { return Task.CompletedTask; };
|
public event Action<ulong, Optional<IMessage>> MessageDeleted = delegate { };
|
||||||
public event Func<SocketUser, SocketGuild, Task> UserBanned = delegate { return Task.CompletedTask; };
|
public event Action<IUser, IGuild> UserBanned = delegate { };
|
||||||
public event Func<SocketUser, SocketGuild, Task> UserUnbanned = delegate { return Task.CompletedTask; };
|
public event Action<IUser, IGuild> UserUnbanned = delegate { };
|
||||||
public event Func<Optional<SocketGuild>, SocketUser, SocketPresence, SocketPresence, Task> UserPresenceUpdated = delegate { return Task.CompletedTask; };
|
public event Action<IGuildUser, IPresence, IPresence> UserPresenceUpdated = delegate { };
|
||||||
public event Func<SocketUser, SocketVoiceState, SocketVoiceState, Task> UserVoiceStateUpdated = delegate { return Task.CompletedTask; };
|
public event Action<IUser, IVoiceState, IVoiceState> UserVoiceStateUpdated = delegate { };
|
||||||
public event Func<SocketChannel, Task> ChannelCreated = delegate { return Task.CompletedTask; };
|
public event Action<IChannel> ChannelCreated = delegate { };
|
||||||
public event Func<SocketChannel, Task> ChannelDestroyed = delegate { return Task.CompletedTask; };
|
public event Action<IChannel> ChannelDestroyed = delegate { };
|
||||||
public event Func<SocketChannel, SocketChannel, Task> ChannelUpdated = delegate { return Task.CompletedTask; };
|
public event Action<IChannel, IChannel> ChannelUpdated = delegate { };
|
||||||
public event Func<Exception, Task> Disconnected = delegate { return Task.CompletedTask; };
|
public event Action<Exception> Disconnected = delegate { };
|
||||||
|
|
||||||
|
private uint _connectedCount = 0;
|
||||||
|
private uint _downloadedCount = 0;
|
||||||
|
|
||||||
private IReadOnlyList<DiscordSocketClient> Clients { get; }
|
private IReadOnlyList<DiscordSocketClient> Clients { get; }
|
||||||
|
|
||||||
public ShardedDiscordClient (DiscordSocketConfig discordSocketConfig)
|
public ShardedDiscordClient(DiscordSocketConfig discordSocketConfig)
|
||||||
{
|
{
|
||||||
_log = LogManager.GetCurrentClassLogger();
|
_log = LogManager.GetCurrentClassLogger();
|
||||||
this.discordSocketConfig = discordSocketConfig;
|
this.discordSocketConfig = discordSocketConfig;
|
||||||
@ -41,18 +45,24 @@ namespace NadekoBot
|
|||||||
discordSocketConfig.ShardId = i;
|
discordSocketConfig.ShardId = i;
|
||||||
var client = new DiscordSocketClient(discordSocketConfig);
|
var client = new DiscordSocketClient(discordSocketConfig);
|
||||||
clientList.Add(client);
|
clientList.Add(client);
|
||||||
client.UserJoined += async arg1 => await UserJoined(arg1);
|
client.UserJoined += arg1 => { UserJoined(arg1); return Task.CompletedTask; };
|
||||||
client.MessageReceived += async arg1 => await MessageReceived(arg1);
|
client.MessageReceived += arg1 =>
|
||||||
client.UserLeft += async arg1 => await UserLeft(arg1);
|
{
|
||||||
client.UserUpdated += async (arg1, gu2) => await UserUpdated(arg1, gu2);
|
if (arg1.Author == null || arg1.Author.IsBot)
|
||||||
client.MessageUpdated += async (arg1, m2) => await MessageUpdated(arg1, m2);
|
return Task.CompletedTask; MessageReceived(arg1);
|
||||||
client.MessageDeleted += async (arg1, arg2) => await MessageDeleted(arg1, arg2);
|
return Task.CompletedTask;
|
||||||
client.UserBanned += async (arg1, arg2) => await UserBanned(arg1, arg2);
|
};
|
||||||
client.UserPresenceUpdated += async (arg1, arg2, arg3, arg4) => await UserPresenceUpdated(arg1, arg2, arg3, arg4);
|
client.UserLeft += arg1 => { UserLeft(arg1); return Task.CompletedTask; };
|
||||||
client.UserVoiceStateUpdated += async (arg1, arg2, arg3) => await UserVoiceStateUpdated(arg1, arg2, arg3);
|
client.UserUpdated += (arg1, gu2) => { UserUpdated(arg1, gu2); return Task.CompletedTask; };
|
||||||
client.ChannelCreated += async arg => await ChannelCreated(arg);
|
client.MessageUpdated += (arg1, m2) => { MessageUpdated(arg1, m2); return Task.CompletedTask; };
|
||||||
client.ChannelDestroyed += async arg => await ChannelDestroyed(arg);
|
client.MessageDeleted += (arg1, arg2) => { MessageDeleted(arg1, arg2); return Task.CompletedTask; };
|
||||||
client.ChannelUpdated += async (arg1, arg2) => await ChannelUpdated(arg1, arg2);
|
client.UserBanned += (arg1, arg2) => { UserBanned(arg1, arg2); return Task.CompletedTask; };
|
||||||
|
client.UserUnbanned += (arg1, arg2) => { UserUnbanned(arg1, arg2); return Task.CompletedTask; };
|
||||||
|
client.UserPresenceUpdated += (arg1, arg2, arg3) => { UserPresenceUpdated(arg1, arg2, arg3); return Task.CompletedTask; };
|
||||||
|
client.UserVoiceStateUpdated += (arg1, arg2, arg3) => { UserVoiceStateUpdated(arg1, arg2, arg3); return Task.CompletedTask; };
|
||||||
|
client.ChannelCreated += arg => { ChannelCreated(arg); return Task.CompletedTask; };
|
||||||
|
client.ChannelDestroyed += arg => { ChannelDestroyed(arg); return Task.CompletedTask; };
|
||||||
|
client.ChannelUpdated += (arg1, arg2) => { ChannelUpdated(arg1, arg2); return Task.CompletedTask; };
|
||||||
|
|
||||||
_log.Info($"Shard #{i} initialized.");
|
_log.Info($"Shard #{i} initialized.");
|
||||||
}
|
}
|
||||||
@ -80,12 +90,15 @@ namespace NadekoBot
|
|||||||
|
|
||||||
internal async Task ConnectAsync()
|
internal async Task ConnectAsync()
|
||||||
{
|
{
|
||||||
|
|
||||||
foreach (var c in Clients)
|
foreach (var c in Clients)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
var sw = Stopwatch.StartNew();
|
||||||
await c.ConnectAsync().ConfigureAwait(false);
|
await c.ConnectAsync().ConfigureAwait(false);
|
||||||
_log.Info($"Shard #{c.ShardId} connected.");
|
sw.Stop();
|
||||||
|
_log.Info($"Shard #{c.ShardId} connected after {sw.Elapsed.TotalSeconds:F2}s ({++_connectedCount}/{Clients.Count})");
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
@ -101,7 +114,13 @@ namespace NadekoBot
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal Task DownloadAllUsersAsync() =>
|
internal Task DownloadAllUsersAsync() =>
|
||||||
Task.WhenAll(Clients.Select(async c => { await c.DownloadAllUsersAsync().ConfigureAwait(false); _log.Info($"Shard #{c.ShardId} downloaded {c.Guilds.Sum(g => g.Users.Count)} users."); }));
|
Task.WhenAll(Clients.Select(async c =>
|
||||||
|
{
|
||||||
|
var sw = Stopwatch.StartNew();
|
||||||
|
await c.DownloadAllUsersAsync().ConfigureAwait(false);
|
||||||
|
sw.Stop();
|
||||||
|
_log.Info($"Shard #{c.ShardId} downloaded {c.GetGuilds().Sum(g => g.GetUsers().Count)} users after {sw.Elapsed.TotalSeconds:F2}s ({++_downloadedCount}/{Clients.Count}).");
|
||||||
|
}));
|
||||||
|
|
||||||
public Task SetGame(string game) => Task.WhenAll(Clients.Select(ms => ms.SetGame(game)));
|
public Task SetGame(string game) => Task.WhenAll(Clients.Select(ms => ms.SetGame(game)));
|
||||||
|
|
||||||
|
@ -23,6 +23,26 @@ namespace NadekoBot.Extensions
|
|||||||
http.DefaultRequestHeaders.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
|
http.DefaultRequestHeaders.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static EmbedBuilder WithImageUrl(this EmbedBuilder eb, string url) =>
|
||||||
|
eb.WithImage(eib => eib.WithUrl(url));
|
||||||
|
|
||||||
|
public static EmbedBuilder WithOkColor(this EmbedBuilder eb) =>
|
||||||
|
eb.WithColor(NadekoBot.OkColor);
|
||||||
|
|
||||||
|
public static EmbedBuilder WithErrorColor(this EmbedBuilder eb) =>
|
||||||
|
eb.WithColor(NadekoBot.ErrorColor);
|
||||||
|
|
||||||
|
public static IMessage DeleteAfter(this IUserMessage msg, int seconds)
|
||||||
|
{
|
||||||
|
Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await Task.Delay(seconds * 1000);
|
||||||
|
try { await msg.DeleteAsync().ConfigureAwait(false); }
|
||||||
|
catch { }
|
||||||
|
});
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
public static string GetPrefix(this ModuleInfo module) => NadekoBot.ModulePrefixes[module.GetTopLevelModule().Name];
|
public static string GetPrefix(this ModuleInfo module) => NadekoBot.ModulePrefixes[module.GetTopLevelModule().Name];
|
||||||
|
|
||||||
public static ModuleInfo GetTopLevelModule(this ModuleInfo module) {
|
public static ModuleInfo GetTopLevelModule(this ModuleInfo module) {
|
||||||
@ -111,16 +131,16 @@ namespace NadekoBot.Extensions
|
|||||||
public static Task<IUserMessage> EmbedAsync(this IMessageChannel ch, EmbedBuilder embed, string msg = "")
|
public static Task<IUserMessage> EmbedAsync(this IMessageChannel ch, EmbedBuilder embed, string msg = "")
|
||||||
=> ch.SendMessageAsync(msg, embed: embed);
|
=> ch.SendMessageAsync(msg, embed: embed);
|
||||||
|
|
||||||
public static Task<IUserMessage> SendErrorAsync(this IMessageChannel ch, string title, string error, string url = null)
|
public static Task<IUserMessage> SendErrorAsync(this IMessageChannel ch, string title, string error, string url = null, string error = null)
|
||||||
=> ch.SendMessageAsync("", embed: new EmbedBuilder().WithColor(NadekoBot.ErrorColor).WithDescription(error)
|
=> ch.SendMessageAsync("", embed: new EmbedBuilder().WithColor(NadekoBot.ErrorColor).WithDescription(error)
|
||||||
.WithTitle(title).WithUrl(url));
|
.WithTitle(title).WithUrl(url).WithFooter(efb => efb.WithText(error));
|
||||||
|
|
||||||
public static Task<IUserMessage> SendErrorAsync(this IMessageChannel ch, string error)
|
public static Task<IUserMessage> SendErrorAsync(this IMessageChannel ch, string error)
|
||||||
=> ch.SendMessageAsync("", embed: new EmbedBuilder().WithColor(NadekoBot.OkColor).WithDescription(error));
|
=> ch.SendMessageAsync("", embed: new EmbedBuilder().WithColor(NadekoBot.OkColor).WithDescription(error));
|
||||||
|
|
||||||
public static Task<IUserMessage> SendConfirmAsync(this IMessageChannel ch, string title, string text, string url = null)
|
public static Task<IUserMessage> SendConfirmAsync(this IMessageChannel ch, string title, string text, string url = null, string footer = null)
|
||||||
=> ch.SendMessageAsync("", embed: new EmbedBuilder().WithColor(NadekoBot.OkColor).WithDescription(text)
|
=> ch.SendMessageAsync("", embed: new EmbedBuilder().WithColor(NadekoBot.OkColor).WithDescription(text)
|
||||||
.WithTitle(title).WithUrl(url));
|
.WithTitle(title).WithUrl(url).WithFooter(efb => efb.WithText(footer));
|
||||||
|
|
||||||
public static Task<IUserMessage> SendConfirmAsync(this IMessageChannel ch, string text)
|
public static Task<IUserMessage> SendConfirmAsync(this IMessageChannel ch, string text)
|
||||||
=> ch.SendMessageAsync("", embed: new EmbedBuilder().WithColor(NadekoBot.OkColor).WithDescription(text));
|
=> ch.SendMessageAsync("", embed: new EmbedBuilder().WithColor(NadekoBot.OkColor).WithDescription(text));
|
||||||
|
@ -2579,7 +2579,7 @@
|
|||||||
"ImageUrl": "https://www.randomlists.com/img/things/drill_press.jpg"
|
"ImageUrl": "https://www.randomlists.com/img/things/drill_press.jpg"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Word": "earser",
|
"Word": "eraser",
|
||||||
"ImageUrl": "https://www.randomlists.com/img/things/earser.jpg"
|
"ImageUrl": "https://www.randomlists.com/img/things/earser.jpg"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
File diff suppressed because it is too large
Load Diff
1
src/NadekoBot/data/trivia_questions.json
Normal file
1
src/NadekoBot/data/trivia_questions.json
Normal file
File diff suppressed because one or more lines are too long
@ -20,8 +20,9 @@
|
|||||||
"VideoLibrary": "1.3.4",
|
"VideoLibrary": "1.3.4",
|
||||||
"CoreCLR-NCalc": "2.1.2",
|
"CoreCLR-NCalc": "2.1.2",
|
||||||
"Google.Apis.Urlshortener.v1": "1.19.0.138",
|
"Google.Apis.Urlshortener.v1": "1.19.0.138",
|
||||||
"Google.Apis.YouTube.v3": "1.19.0.655",
|
"Google.Apis.YouTube.v3": "1.20.0.701",
|
||||||
"ImageSharp": "1.0.0-alpha-*",
|
"Google.Apis.Customsearch.v1": "1.20.0.466",
|
||||||
|
"ImageSharp": "1.0.0-alpha-000079",
|
||||||
"Microsoft.EntityFrameworkCore": "1.1.0",
|
"Microsoft.EntityFrameworkCore": "1.1.0",
|
||||||
"Microsoft.EntityFrameworkCore.Design": "1.1.0",
|
"Microsoft.EntityFrameworkCore.Design": "1.1.0",
|
||||||
"Microsoft.EntityFrameworkCore.Sqlite": "1.1.0",
|
"Microsoft.EntityFrameworkCore.Sqlite": "1.1.0",
|
||||||
|
Loading…
Reference in New Issue
Block a user