mirror of
https://gitlab.com/Kwoth/nadekobot.git
synced 2025-09-11 01:38:27 -04:00
Compare commits
30 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
2d92424dd4 | ||
|
ffba03adbe | ||
|
76ebb87fb5 | ||
|
4a50c30c56 | ||
|
e70a91ae60 | ||
|
3470762b30 | ||
|
2cbb4ecd3d | ||
|
619bee811d | ||
|
a09be96200 | ||
|
81254ed5f6 | ||
|
d82e3bd444 | ||
|
9011646b02 | ||
|
2a1f45819d | ||
|
d2d0cb9e03 | ||
|
ed039977c2 | ||
|
786ede3290 | ||
|
3edcca4927 | ||
|
0635a0ddc8 | ||
|
cb514e4219 | ||
|
cccb37854c | ||
|
35d549f4e6 | ||
|
c0d81a5d9c | ||
|
711d6c7caa | ||
|
cea944cdb8 | ||
|
2d70ea487e | ||
|
9f65f979fd | ||
|
c12b41ddd1 | ||
|
a5f9ac1540 | ||
|
8c5214def2 | ||
|
467a8bdaf6 |
44
CHANGELOG.md
44
CHANGELOG.md
@@ -4,6 +4,49 @@ Experimental changelog. Mostly based on [keepachangelog](https://keepachangelog.
|
||||
|
||||
## Unreleased
|
||||
|
||||
## [3.0.5] - 20.09.2021
|
||||
|
||||
### Fixed
|
||||
- Fixed images not automatically reloading on startup if the keys don't exist
|
||||
- Fixed `.logserver` - it should no longer throw an exception if you had no logsettings previously
|
||||
|
||||
## [3.0.4] - 16.09.2021
|
||||
|
||||
### Added
|
||||
- Fully translated to Brazilian Portuguese 🎉
|
||||
- Added `%server.boosters%` and `%server.boost_level%` placeholders
|
||||
- Added `DmHelpTextKeywords` to `data/bot.yml`
|
||||
- Bot now sends dm help text ONLY if the message contains one of the keywords specified
|
||||
- If no keywords are specified, bot will reply to every DM (like before)
|
||||
|
||||
### Fixed
|
||||
- Possible fix for `.repeat` bug
|
||||
- Slight adjustment for repeater logic
|
||||
- Timer should no longer increase on some repeaters
|
||||
- Repeaters should no longer have periods when they're missing from the list
|
||||
- Fixed several commands which used error color for success confirmation messages
|
||||
|
||||
## [3.0.3] - 15.09.2021
|
||||
|
||||
### Added
|
||||
|
||||
- Added `.massban` to ban multiple people at once. 30 second cooldown
|
||||
- Added `.youtubeuploadnotif` / `.yun` as a shortcut for subscribing to a youtube channel's rss feed
|
||||
- Added `.imageonlychannel` / `.imageonly` to prevent users from posting anything but images in the channel
|
||||
- Added `.config games hangman.currency_reward` and a property with the same name in games.yml
|
||||
- If set, users will gain the specified amount of currency for each hangman win
|
||||
- Fully translated to Spanish, Russian and Ukrainian 🎉
|
||||
|
||||
### Changed
|
||||
|
||||
- Ban `.warnp` will now prune user's messages
|
||||
|
||||
### Fixed
|
||||
|
||||
- `.boostmsg` will now properly show boost, and not greet message
|
||||
|
||||
## [3.0.2] - 12.09.2021
|
||||
|
||||
### Added
|
||||
|
||||
- `.rero` now optionally takes a message id to which to attach the reaction roles
|
||||
@@ -20,6 +63,7 @@ Experimental changelog. Mostly based on [keepachangelog](https://keepachangelog.
|
||||
### Fixed
|
||||
|
||||
- `.timely` will now correctly use `Ok` color
|
||||
- Fixed `.log` commands
|
||||
|
||||
### Removed
|
||||
|
||||
|
@@ -1,3 +1,35 @@
|
||||
# Setting up NadekoBot with Docker
|
||||
|
||||
Soon:tm:
|
||||
# DO NOT USE YET - WORK IN PROGRESS
|
||||
|
||||
Upgrade from 2.x to v3 does not work because the file is mount readonly
|
||||
|
||||
### Docker Compose
|
||||
```yml
|
||||
version: "3.7"
|
||||
services:
|
||||
nadeko:
|
||||
image: registry.gitlab.com/veovis/nadekobot:v3-docker
|
||||
depends_on:
|
||||
- redis
|
||||
environment:
|
||||
TZ: Europe/Paris
|
||||
#NadekoBot_RedisOptions: redis,name=nadeko
|
||||
#NadekoBot_ShardRunCommand: dotnet
|
||||
#NadekoBot_ShardRunArguments: /app/NadekoBot.dll {0} {1}
|
||||
volumes:
|
||||
- /srv/nadeko/conf/creds.yml:/app/creds.yml:ro
|
||||
- /srv/nadeko/data:/app/data
|
||||
|
||||
redis:
|
||||
image: redis:4-alpine
|
||||
sysctls:
|
||||
- net.core.somaxconn=511
|
||||
command: redis-server --maxmemory 32M --maxmemory-policy volatile-lru
|
||||
volumes:
|
||||
- /srv/nadeko/redis-data:/data
|
||||
```
|
||||
### Updating
|
||||
- `cd /srv/nadeko`
|
||||
- `docker-compose pull`
|
||||
- `docker-compose up -d`
|
||||
|
@@ -16,12 +16,12 @@ Open Terminal (if you're on an installation with a window manager) and navigate
|
||||
4. Exit the installer in order to set up your `creds.yml`
|
||||
5. Copy the creds.yml template `cp nadekobot/output/creds_example.yml nadekobot/output/creds.yml`
|
||||
6. Open `nadekobot/output/creds.yml` with your favorite text editor. We will use nano here
|
||||
- `nano nadekobot/output/creds.yml`
|
||||
- `nano nadekobot/output/creds.yml`
|
||||
7. [Enter your bot's token](../../creds-guide)
|
||||
- After you're done, you can close nano (and save the file) by inputting, in order
|
||||
- `CTRL` + `X`
|
||||
- `Y`
|
||||
- `Enter`
|
||||
- After you're done, you can close nano (and save the file) by inputting, in order
|
||||
- `CTRL` + `X`
|
||||
- `Y`
|
||||
- `Enter`
|
||||
8. Run the bot (type `3` and press enter)
|
||||
|
||||
##### Update Instructions
|
||||
@@ -37,51 +37,51 @@ Open Terminal (if you're on an installation with a window manager) and navigate
|
||||
##### Installation Instructions
|
||||
|
||||
1. Download the latest release from <https://gitlab.com/Kwoth/nadekobot/-/releases>
|
||||
- Look for the file called "X.XX.X-linux-x64-build.tar" (where X.XX.X is a series of numbers) and download it
|
||||
- Look for the file called "X.XX.X-linux-x64-build.tar" (where X.XX.X is a series of numbers) and download it
|
||||
2. Untar it
|
||||
- ⚠ Make sure that you change X.XX.X to the same series of numbers as in step 1!
|
||||
- `tar xf X.XX.X-linux-x64-build.tar`
|
||||
- ⚠ Make sure that you change X.XX.X to the same series of numbers as in step 1!
|
||||
- `tar xf X.XX.X-linux-x64-build.tar`
|
||||
3. Rename the `nadekobot-linux-x64` to `nadekobot`
|
||||
- `mv nadekobot-linux-x64 nadekobot`
|
||||
- `mv nadekobot-linux-x64 nadekobot`
|
||||
4. Move into nadekobot directory and make NadekoBot executable
|
||||
- `cd nadekobot && chmod +x NadekoBot`
|
||||
- `cd nadekobot && chmod +x NadekoBot`
|
||||
5. Copy the creds.yml template
|
||||
- `cp creds_example.yml creds.yml`
|
||||
- `cp creds_example.yml creds.yml`
|
||||
6. Open `creds.yml` with your favorite text editor. We will use nano here
|
||||
- `nano nadekobot/output/creds.yml`
|
||||
- `nano nadekobot/output/creds.yml`
|
||||
8. [Enter your bot's token](#creds-guide)
|
||||
- After you're done, you can close nano (and save the file) by inputting, in order
|
||||
- `CTRL` + `X`
|
||||
- `Y`
|
||||
- `Enter`
|
||||
- After you're done, you can close nano (and save the file) by inputting, in order
|
||||
- `CTRL` + `X`
|
||||
- `Y`
|
||||
- `Enter`
|
||||
9. Run the bot
|
||||
- `./NadekoBot`
|
||||
- `./NadekoBot`
|
||||
|
||||
##### Update Instructions
|
||||
|
||||
1. Stop the bot
|
||||
2. Download the latest release from <https://gitlab.com/Kwoth/nadekobot/-/releases>
|
||||
- Look for the file called "X.XX.X-linux-x64-build.tar" (where X.XX.X is a series of numbers) and download it
|
||||
- Look for the file called "x.x.x-linux-x64-build.tar" (where `X.X.X` is a version, for example 3.0.4) and download it
|
||||
3. Untar it
|
||||
- ⚠ Make sure that you change X.XX.X to the same series of numbers as in step 2!
|
||||
- `tar xf 2.99.8-linux-x64-build.tar`
|
||||
- ⚠ Make sure that you change `X.X.X` to the same series of numbers as in step 2!
|
||||
- `tar xf x.x.x-linux-x64-build.tar`
|
||||
4. Rename the old nadekobot directory to nadekobot-old (remove your old backup first if you have one, or back it up under a different name)
|
||||
- `rm -rf nadekobot-old 2>/dev/null`
|
||||
- `mv nadekobot nadekobot-old`
|
||||
- `rm -rf nadekobot-old 2>/dev/null`
|
||||
- `mv nadekobot nadekobot-old`
|
||||
5. Rename the new nadekobot directory to nadekobot
|
||||
- `mv nadekobot-linux-x64 nadekobot`
|
||||
- `mv nadekobot-linux-x64 nadekobot`
|
||||
6. Remove old strings and aliases to avoid overwriting the updated versions of those files
|
||||
- ⚠ If you've modified said files, back them up instead
|
||||
- `rm nadekobot-old/data/aliases.yml`
|
||||
- `rm -r nadekobot-old/data/strings`
|
||||
- ⚠ If you've modified said files, back them up instead
|
||||
- `rm nadekobot-old/data/aliases.yml`
|
||||
- `rm -r nadekobot-old/data/strings`
|
||||
7. Copy old data
|
||||
- `cp -RT nadekobot-old/data/ nadekobot/data/`
|
||||
- `cp -RT nadekobot-old/data/ nadekobot/data`
|
||||
8. Copy creds.yml
|
||||
- `cp nadekobot-old/creds.yml nadekobot/`
|
||||
- `cp nadekobot-old/creds.yml nadekobot/`
|
||||
9. Move into nadekobot directory and make the NadekoBot executable
|
||||
- `cd nadekobot && chmod +x NadekoBot`
|
||||
- `cd nadekobot && chmod +x NadekoBot`
|
||||
10. Run the bot
|
||||
- `./NadekoBot`
|
||||
- `./NadekoBot`
|
||||
|
||||
🎉 Enjoy
|
||||
|
||||
@@ -95,7 +95,135 @@ mv nadekobot nadekobot-old && \
|
||||
mv nadekobot-linux-x64 nadekobot && \
|
||||
rm nadekobot-old/data/aliases.yml && \
|
||||
rm -r nadekobot-old/data/strings && \
|
||||
cp -RT nadekobot-old/data/ nadekobot/data/ && \
|
||||
cp -RT nadekobot-old/data/ nadekobot/data && \
|
||||
cp nadekobot-old/creds.yml nadekobot/ && \
|
||||
cd nadekobot && chmod +x NadekoBot
|
||||
```
|
||||
|
||||
## Running Nadeko
|
||||
|
||||
While there are two run modes built into the installer, these options only run Nadeko within the current session. Below are 3 methods of running Nadeko as a background process.
|
||||
|
||||
### Tmux (Preferred Method)
|
||||
|
||||
Using `tmux` is the simplest method, and is therefore recommended for most users.
|
||||
|
||||
1. Start a tmux session:
|
||||
- `tmux`
|
||||
2. Navigate to the project's root directory
|
||||
- Project root directory location example: `/home/user/nadekobot/`
|
||||
3. Enter the `output` directory:
|
||||
- `cd output`
|
||||
4. Run the bot using:
|
||||
- `dotnet NadekoBot.dll`
|
||||
5. Detatch the tmux session:
|
||||
- Press `Ctrl` + `B`
|
||||
- Then press `D`
|
||||
|
||||
Nadeko should now be running in the background of your system. To re-open the tmux session to either update, restart, or whatever, execute `tmux a`.
|
||||
|
||||
### Systemd
|
||||
|
||||
Compared to using tmux, this method requires a little bit more work to set up, but has the benefit of allowing Nadeko to automatically start back up after a system reboot or the execution of the `.die` command.
|
||||
|
||||
1. Navigate to the project's root directory
|
||||
- Project root directory location example: `/home/user/nadekobot/`
|
||||
2. Use the following command to create a service that will be used to start Nadeko:
|
||||
|
||||
```bash
|
||||
echo "[Unit]
|
||||
Description=NadekoBot service
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=$USER
|
||||
WorkingDirectory=$PWD/output
|
||||
# If you want Nadeko to be compiled prior to every startup, uncomment the lines
|
||||
# below. Note that it's not neccessary unless you are personally modifying the
|
||||
# source code.
|
||||
#ExecStartPre=/usr/bin/dotnet build ../src/NadekoBot/NadekoBot.csproj -c Release -o output/
|
||||
ExecStart=/usr/bin/dotnet NadekoBot.dll
|
||||
StandardOutput=syslog
|
||||
StandardError=syslog
|
||||
SyslogIdentifier=NadekoBot
|
||||
Restart=always
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target" | sudo tee /etc/systemd/system/nadeko.service
|
||||
```
|
||||
|
||||
3. Make the new service available:
|
||||
- `sudo systemctl daemon-reload`
|
||||
4. Start Nadeko:
|
||||
- `sudo systemctl start nadeko.service && sudo systemctl enable nadeko.service`
|
||||
|
||||
|
||||
### Systemd + Script
|
||||
|
||||
This method is similar to the one above, but requires one extra step, with the added benefit of better error logging and control over what happens before and after the startup of Nadeko.
|
||||
|
||||
1. Locate the project and move to its parent directory
|
||||
- Project location example: `/home/user/nadekobot/`
|
||||
- Parent directory example: `/home/user/`
|
||||
2. Use the following command to create a service that will be used to execute `NadekoRun.sh`:
|
||||
|
||||
```bash
|
||||
echo "[Unit]
|
||||
Description=NadekoBot service
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=$USER
|
||||
WorkingDirectory=$PWD
|
||||
ExecStart=/bin/bash NadekoRun.sh
|
||||
StandardOutput=syslog
|
||||
StandardError=syslog
|
||||
SyslogIdentifier=NadekoBot
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target" | sudo tee /etc/systemd/system/nadeko.service
|
||||
```
|
||||
|
||||
3. Make the new service available:
|
||||
- `sudo systemctl daemon-reload`
|
||||
4. Use the following command to create a script that will be used to start Nadeko:
|
||||
|
||||
```bash
|
||||
echo "#\!/bin/bash
|
||||
|
||||
echo \"\"
|
||||
echo \"Running NadekoBot in the background with auto restart\"
|
||||
youtube-dl -U
|
||||
|
||||
# If you want Nadeko to be compiled prior to every startup, uncomment the lines
|
||||
# below. Note that it's not neccessary unless you are personally modifying the
|
||||
# source code.
|
||||
#echo \"Compiling NadekoBot...\"
|
||||
#cd \"$PWD\"/nadekobot
|
||||
#dotnet build src/NadekoBot/NadekoBot.csproj -c Release -o output/
|
||||
|
||||
echo \"Starting NadekoBot...\"
|
||||
|
||||
while true; do
|
||||
{
|
||||
cd \"$PWD\"/nadekobot/output
|
||||
dotnet NadekoBot.dll
|
||||
## If a non-zero exit code is produced, exit this script.
|
||||
} || {
|
||||
error_code=\"\$?\"
|
||||
echo \"An error occurred when trying to start NadekBot\"
|
||||
echo \"EXIT CODE: \$?\"
|
||||
exit \"\$error_code\"
|
||||
}
|
||||
|
||||
youtube-dl -U
|
||||
echo \"Restarting NadekoBot...\"
|
||||
done
|
||||
|
||||
echo \"Stopping NadekoBot...\"" > NadekoRun.sh
|
||||
```
|
||||
|
||||
5. Start Nadeko:
|
||||
- `sudo systemctl start nadeko.service && sudo systemctl enable nadeko.service`
|
@@ -80,6 +80,7 @@ You can still install them manually:
|
||||
Open PowerShell (press windows button on your keyboard and type powershell, it should show up; alternatively, right click the start menu and select Windows PowerShell), and navigate to the location where you want to install the bot (for example `cd ~/Desktop/`)
|
||||
|
||||
1. `git clone https://gitlab.com/kwoth/nadekobot -b v3 --depth 1`
|
||||
2. `cd nadekobot`
|
||||
3. `dotnet publish -c Release -o output/ src/NadekoBot/`
|
||||
4. `cd output && cp creds_example.yml creds.yml`
|
||||
5. Open `creds.yml` with your favorite text editor (Please don't use Notepad or WordPad. You can use Notepad++, VSCode, Atom, Sublime, or something similar)
|
||||
@@ -91,16 +92,30 @@ Open PowerShell (press windows button on your keyboard and type powershell, it s
|
||||
|
||||
Open PowerShell as described above and run the following commands:
|
||||
|
||||
1. Navigate to your bot's folder, for example `cd ~/Desktop/nadekobot/src/NadekoBot`
|
||||
2. Pull the latest updates (this will fail if you have custom code changes).
|
||||
- If you don't have custom code changes, just run `git pull`
|
||||
- If you do have custom code changes, You have 3 options
|
||||
- Undo all changes with `git checkout -- * && git pull`
|
||||
- Stash changes and try to re-apply them `git stash && git pull && git stash apply`
|
||||
- Commit your changes and resolve merge conflicts `git add . && git commit -m "My commit message" && git pull`
|
||||
3. Re-run the bot `dotnet run -c Release`
|
||||
1. Stop the bot
|
||||
- ⚠️ Make sure you don't have your database, credentials or any other nadekobot folder open in some application, this might prevent some of the steps from executing succesfully
|
||||
2. Navigate to your bot's folder, example:
|
||||
- `cd ~/Desktop/nadekobot`
|
||||
3. Pull the new version
|
||||
- `git pull`
|
||||
- ⚠️ If this fails, you may want to stash or remove your code changes if you don't know how to resolve merge conflicts
|
||||
4. **Backup** old output in case your data is overwritten
|
||||
- `cp -r -fo output/ output-old`
|
||||
5. Build the bot again
|
||||
- `dotnet publish -c Release -o output/ src/NadekoBot/`
|
||||
6. Remove old strings and aliases to avoid overwriting the updated versions of those files
|
||||
- ⚠ If you've modified said files, back them up instead
|
||||
- `rm output-old/data/aliases.yml`
|
||||
- `rm -r output-old/data/strings`
|
||||
7. Copy old data
|
||||
- `cp -Recurse .\output-old\data\ .\output\ -Force`
|
||||
8. Copy creds.yml
|
||||
- `cp output-old/creds.yml output/`
|
||||
9. Run the bot
|
||||
- `cd output`
|
||||
- `dotnet NadekoBot.dll`
|
||||
|
||||
⚠ You're expected to understand that your database will be in `bin/Release/<framework>/data/`, and if `<framework>` gets changed in the future, you will have to move your database manually.
|
||||
🎉 Enjoy
|
||||
|
||||
#### Music prerequisites
|
||||
In order to use music commands, you need ffmpeg and youtube-dl installed.
|
||||
|
@@ -2,12 +2,10 @@
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using AngleSharp.Common;
|
||||
using Discord.Commands;
|
||||
using NadekoBot.Common.Attributes;
|
||||
using NadekoBot.Services;
|
||||
using NadekoBot.Modules;
|
||||
using YamlDotNet.Serialization;
|
||||
|
||||
namespace NadekoBot.Tests
|
||||
{
|
||||
|
@@ -102,7 +102,6 @@ namespace NadekoBot
|
||||
.AddSingleton(Client) // discord socket client
|
||||
.AddSingleton(_commandService)
|
||||
.AddSingleton(this)
|
||||
.AddSingleton<IDataCache, RedisCache>()
|
||||
.AddSingleton<ISeria, JsonSeria>()
|
||||
.AddSingleton<IPubSub, RedisPubSub>()
|
||||
.AddSingleton<IConfigSeria, YamlSeria>()
|
||||
@@ -132,10 +131,18 @@ namespace NadekoBot
|
||||
}
|
||||
else
|
||||
{
|
||||
svcs.AddSingleton<ICoordinator, RemoteGrpcCoordinator>()
|
||||
.AddSingleton<IReadyExecutor>(x => (IReadyExecutor)x.GetRequiredService<ICoordinator>());
|
||||
svcs.AddSingleton<RemoteGrpcCoordinator>()
|
||||
.AddSingleton<ICoordinator>(x => x.GetRequiredService<RemoteGrpcCoordinator>())
|
||||
.AddSingleton<IReadyExecutor>(x => x.GetRequiredService<RemoteGrpcCoordinator>());
|
||||
}
|
||||
|
||||
svcs.AddSingleton<RedisLocalDataCache>()
|
||||
.AddSingleton<ILocalDataCache>(x => x.GetRequiredService<RedisLocalDataCache>())
|
||||
.AddSingleton<RedisImagesCache>()
|
||||
.AddSingleton<IImageCache>(x => x.GetRequiredService<RedisImagesCache>())
|
||||
.AddSingleton<IReadyExecutor>(x => x.GetRequiredService<RedisImagesCache>())
|
||||
.AddSingleton<IDataCache, RedisCache>();
|
||||
|
||||
svcs.Scan(scan => scan
|
||||
.FromAssemblyOf<IReadyExecutor>()
|
||||
.AddClasses(classes => classes.AssignableToAny(
|
||||
|
@@ -1,26 +0,0 @@
|
||||
namespace NadekoBot.Common
|
||||
{
|
||||
public enum BotConfigEditType
|
||||
{
|
||||
/// <summary>
|
||||
/// The amount of currency awarded to the winner of the trivia game.
|
||||
/// Default is 0.
|
||||
/// </summary>
|
||||
TriviaCurrencyReward,
|
||||
/// <summary>
|
||||
/// Users can't start trivia games which have smaller win requirement than specified by this setting.
|
||||
/// Default is 0.
|
||||
/// </summary>
|
||||
MinimumTriviaWinReq,
|
||||
/// <summary>
|
||||
/// The amount of XP the user receives when they send a message (which is not too short).
|
||||
/// Default is 3.
|
||||
/// </summary>
|
||||
XpPerMessage,
|
||||
/// <summary>
|
||||
/// This value represents how often the user can receive XP from sending messages.
|
||||
/// Default is 5.
|
||||
/// </summary>
|
||||
XpMinutesTimeout,
|
||||
}
|
||||
}
|
@@ -12,7 +12,7 @@ namespace NadekoBot.Common.Configs
|
||||
public sealed partial class BotConfig : ICloneable<BotConfig>
|
||||
{
|
||||
[Comment(@"DO NOT CHANGE")]
|
||||
public int Version { get; set; }
|
||||
public int Version { get; set; } = 2;
|
||||
|
||||
[Comment(@"Most commands, when executed, have a small colored line
|
||||
next to the response. The color depends whether the command
|
||||
@@ -48,6 +48,11 @@ they will receive this message. Leave empty for no response. The string which wi
|
||||
Supports embeds. How it looks: https://puu.sh/B0BLV.png")]
|
||||
[YamlMember(ScalarStyle = ScalarStyle.Literal)]
|
||||
public string DmHelpText { get; set; }
|
||||
|
||||
[Comment(@"Only users who send a DM to the bot containing one of the specified words will get a DmHelpText response.
|
||||
Case insensitive.
|
||||
Leave empty to reply with DmHelpText to every DM.")]
|
||||
public List<string> DmHelpTextKeywords { get; set; }
|
||||
|
||||
[Comment(@"This is the response for the .h command")]
|
||||
[YamlMember(ScalarStyle = ScalarStyle.Literal)]
|
||||
@@ -89,7 +94,6 @@ See RotatingStatuses submodule in Administration.")]
|
||||
|
||||
public BotConfig()
|
||||
{
|
||||
Version = 1;
|
||||
var color = new ColorConfig();
|
||||
Color = color;
|
||||
DefaultLocale = new CultureInfo("en-US");
|
||||
@@ -127,6 +131,14 @@ See RotatingStatuses submodule in Administration.")]
|
||||
Prefix = ".";
|
||||
RotateStatuses = false;
|
||||
GroupGreets = false;
|
||||
DmHelpTextKeywords = new List<string>()
|
||||
{
|
||||
"help",
|
||||
"commands",
|
||||
"cmds",
|
||||
"module",
|
||||
"can you do"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -89,6 +89,8 @@ namespace NadekoBot.Common.Replacements
|
||||
_reps.TryAdd("%server.id%", () => g is null ? "DM" : g.Id.ToString());
|
||||
_reps.TryAdd("%server.name%", () => g is null ? "DM" : g.Name);
|
||||
_reps.TryAdd("%server.members%", () => g != null && g is SocketGuild sg ? sg.MemberCount.ToString() : "?");
|
||||
_reps.TryAdd("%server.boosters%", () => g.PremiumSubscriptionCount.ToString());
|
||||
_reps.TryAdd("%server.boost_level%", () => ((int)g.PremiumTier).ToString());
|
||||
_reps.TryAdd("%server.time%", () =>
|
||||
{
|
||||
TimeZoneInfo to = TimeZoneInfo.Local;
|
||||
|
@@ -120,6 +120,15 @@ namespace NadekoBot.Db
|
||||
.Include(x => x.IgnoredChannels)
|
||||
.Where(x => x.GuildId == guildId)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (logSetting is null)
|
||||
{
|
||||
ctx.LogSettings.Add(logSetting = new ()
|
||||
{
|
||||
GuildId = guildId
|
||||
});
|
||||
ctx.SaveChanges();
|
||||
}
|
||||
|
||||
return logSetting;
|
||||
}
|
||||
|
8
src/NadekoBot/Db/Models/ImageOnlyChannel.cs
Normal file
8
src/NadekoBot/Db/Models/ImageOnlyChannel.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace NadekoBot.Services.Database.Models
|
||||
{
|
||||
public class ImageOnlyChannel : DbEntity
|
||||
{
|
||||
public ulong GuildId { get; set; }
|
||||
public ulong ChannelId { get; set; }
|
||||
}
|
||||
}
|
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace NadekoBot.Services.Database.Models
|
||||
{
|
||||
|
||||
public class LogSetting : DbEntity
|
||||
{
|
||||
public HashSet<IgnoredLogChannel> IgnoredChannels { get; set; } = new HashSet<IgnoredLogChannel>();
|
||||
|
@@ -2,11 +2,9 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Design;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using NadekoBot.Services;
|
||||
using System;
|
||||
using System.IO;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NadekoBot.Common;
|
||||
using NadekoBot.Db.Models;
|
||||
|
||||
namespace NadekoBot.Services.Database
|
||||
@@ -60,6 +58,7 @@ namespace NadekoBot.Services.Database
|
||||
public DbSet<Repeater> Repeaters { get; set; }
|
||||
public DbSet<Poll> Poll { get; set; }
|
||||
public DbSet<WaifuInfo> WaifuInfo { get; set; }
|
||||
public DbSet<ImageOnlyChannel> ImageOnlyChannels { get; set; }
|
||||
|
||||
public NadekoContext(DbContextOptions<NadekoContext> options) : base(options)
|
||||
{
|
||||
@@ -345,6 +344,10 @@ namespace NadekoBot.Services.Database
|
||||
.IsUnique());
|
||||
|
||||
#endregion
|
||||
|
||||
modelBuilder.Entity<ImageOnlyChannel>(ioc => ioc
|
||||
.HasIndex(x => x.ChannelId)
|
||||
.IsUnique());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
2653
src/NadekoBot/Migrations/20210914180026_image-only-channels.Designer.cs
generated
Normal file
2653
src/NadekoBot/Migrations/20210914180026_image-only-channels.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace NadekoBot.Migrations
|
||||
{
|
||||
public partial class imageonlychannels : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ImageOnlyChannels",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
GuildId = table.Column<ulong>(type: "INTEGER", nullable: false),
|
||||
ChannelId = table.Column<ulong>(type: "INTEGER", nullable: false),
|
||||
DateAdded = table.Column<DateTime>(type: "TEXT", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ImageOnlyChannels", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ImageOnlyChannels_ChannelId",
|
||||
table: "ImageOnlyChannels",
|
||||
column: "ChannelId",
|
||||
unique: true);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "ImageOnlyChannels");
|
||||
}
|
||||
}
|
||||
}
|
@@ -187,6 +187,29 @@ namespace NadekoBot.Migrations
|
||||
b.ToTable("FollowedStream");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NadekoBot.Services.Database.ImageOnlyChannel", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<ulong>("ChannelId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<ulong>("GuildId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ChannelId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("ImageOnlyChannels");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiAltSetting", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
|
@@ -13,13 +13,31 @@ namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
public partial class Administration : NadekoModule<AdministrationService>
|
||||
{
|
||||
private readonly ImageOnlyChannelService _imageOnly;
|
||||
|
||||
public Administration(ImageOnlyChannelService imageOnly)
|
||||
{
|
||||
_imageOnly = imageOnly;
|
||||
}
|
||||
|
||||
public enum List
|
||||
{
|
||||
List = 0,
|
||||
Ls = 0
|
||||
}
|
||||
|
||||
|
||||
[NadekoCommand, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
[BotPerm(GuildPerm.Administrator)]
|
||||
public async Task ImageOnlyChannel(StoopidTime time = null)
|
||||
{
|
||||
var newValue = _imageOnly.ToggleImageOnlyChannel(ctx.Guild.Id, ctx.Channel.Id);
|
||||
if (newValue)
|
||||
await ReplyConfirmLocalizedAsync(strs.imageonly_enable);
|
||||
else
|
||||
await ReplyPendingLocalizedAsync(strs.imageonly_disable);
|
||||
}
|
||||
|
||||
[NadekoCommand, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
|
@@ -90,7 +90,7 @@ namespace NadekoBot.Modules.Administration
|
||||
return;
|
||||
|
||||
await _service.TimedMute(user, ctx.User, time.Time, reason: reason).ConfigureAwait(false);
|
||||
await ReplyErrorLocalizedAsync(strs.user_muted_time(Format.Bold(user.ToString()), (int)time.Time.TotalMinutes));
|
||||
await ReplyConfirmLocalizedAsync(strs.user_muted_time(Format.Bold(user.ToString()), (int)time.Time.TotalMinutes));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -150,7 +150,7 @@ namespace NadekoBot.Modules.Administration
|
||||
return;
|
||||
|
||||
await _service.TimedMute(user, ctx.User, time.Time, MuteType.Chat, reason: reason).ConfigureAwait(false);
|
||||
await ReplyErrorLocalizedAsync(strs.user_chat_mute_time(Format.Bold(user.ToString()), (int)time.Time.TotalMinutes));
|
||||
await ReplyConfirmLocalizedAsync(strs.user_chat_mute_time(Format.Bold(user.ToString()), (int)time.Time.TotalMinutes));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -209,7 +209,7 @@ namespace NadekoBot.Modules.Administration
|
||||
return;
|
||||
|
||||
await _service.TimedMute(user, ctx.User, time.Time, MuteType.Voice, reason: reason).ConfigureAwait(false);
|
||||
await ReplyErrorLocalizedAsync(strs.user_voice_mute_time(Format.Bold(user.ToString()), (int)time.Time.TotalMinutes));
|
||||
await ReplyConfirmLocalizedAsync(strs.user_voice_mute_time(Format.Bold(user.ToString()), (int)time.Time.TotalMinutes));
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
@@ -61,7 +61,7 @@ namespace NadekoBot.Modules.Administration
|
||||
if (msg is null)
|
||||
return;
|
||||
|
||||
await ReplyErrorLocalizedAsync(strs.reprm(msg));
|
||||
await ReplyConfirmLocalizedAsync(strs.reprm(msg));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -28,7 +28,7 @@ namespace NadekoBot.Modules.Administration
|
||||
return;
|
||||
}
|
||||
|
||||
await ReplyErrorLocalizedAsync(strs.protection_not_running("Anti-Alt"));
|
||||
await ReplyConfirmLocalizedAsync(strs.protection_not_running("Anti-Alt"));
|
||||
}
|
||||
|
||||
[NadekoCommand, Aliases]
|
||||
@@ -69,11 +69,11 @@ namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
if (_service.TryStopAntiRaid(ctx.Guild.Id))
|
||||
{
|
||||
return ReplyErrorLocalizedAsync(strs.prot_disable("Anti-Raid"));
|
||||
return ReplyConfirmLocalizedAsync(strs.prot_disable("Anti-Raid"));
|
||||
}
|
||||
else
|
||||
{
|
||||
return ReplyErrorLocalizedAsync(strs.protection_not_running("Anti-Raid"));
|
||||
return ReplyPendingLocalizedAsync(strs.protection_not_running("Anti-Raid"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,11 +145,11 @@ namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
if (_service.TryStopAntiSpam(ctx.Guild.Id))
|
||||
{
|
||||
return ReplyErrorLocalizedAsync(strs.prot_disable("Anti-Spam"));
|
||||
return ReplyConfirmLocalizedAsync(strs.prot_disable("Anti-Spam"));
|
||||
}
|
||||
else
|
||||
{
|
||||
return ReplyErrorLocalizedAsync(strs.protection_not_running("Anti-Spam"));
|
||||
return ReplyPendingLocalizedAsync(strs.protection_not_running("Anti-Spam"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -189,7 +189,7 @@ namespace NadekoBot.Modules.Administration
|
||||
index--;
|
||||
var rr = rrs[index];
|
||||
_service.Remove(ctx.Guild.Id, index);
|
||||
await ReplyErrorLocalizedAsync(strs.reaction_role_removed(index + 1));
|
||||
await ReplyConfirmLocalizedAsync(strs.reaction_role_removed(index + 1));
|
||||
}
|
||||
|
||||
[NadekoCommand, Aliases]
|
||||
|
@@ -24,11 +24,11 @@ namespace NadekoBot.Modules.Administration
|
||||
|
||||
if (newVal)
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.adsarm_enable(Prefix));
|
||||
await ReplyConfirmLocalizedAsync(strs.adsarm_enable(Prefix));
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.adsarm_disable(Prefix));
|
||||
await ReplyConfirmLocalizedAsync(strs.adsarm_disable(Prefix));
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -88,7 +88,7 @@ namespace NadekoBot.Modules.Administration
|
||||
};
|
||||
_service.AddNewAutoCommand(cmd);
|
||||
|
||||
await ReplyErrorLocalizedAsync(strs.autocmd_add(Format.Code(Format.Sanitize(cmdText)), cmd.Interval));
|
||||
await ReplyConfirmLocalizedAsync(strs.autocmd_add(Format.Code(Format.Sanitize(cmdText)), cmd.Interval));
|
||||
}
|
||||
|
||||
[NadekoCommand, Aliases]
|
||||
@@ -226,7 +226,7 @@ namespace NadekoBot.Modules.Administration
|
||||
if (enabled)
|
||||
await ReplyConfirmLocalizedAsync(strs.fwdm_start).ConfigureAwait(false);
|
||||
else
|
||||
await ReplyConfirmLocalizedAsync(strs.fwdm_stop).ConfigureAwait(false);
|
||||
await ReplyPendingLocalizedAsync(strs.fwdm_stop).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Aliases]
|
||||
@@ -238,7 +238,7 @@ namespace NadekoBot.Modules.Administration
|
||||
if (enabled)
|
||||
await ReplyConfirmLocalizedAsync(strs.fwall_start).ConfigureAwait(false);
|
||||
else
|
||||
await ReplyConfirmLocalizedAsync(strs.fwall_stop).ConfigureAwait(false);
|
||||
await ReplyPendingLocalizedAsync(strs.fwall_stop).ConfigureAwait(false);
|
||||
|
||||
}
|
||||
|
||||
@@ -376,7 +376,7 @@ namespace NadekoBot.Modules.Administration
|
||||
var curUser = await ctx.Guild.GetCurrentUserAsync().ConfigureAwait(false);
|
||||
await curUser.ModifyAsync(u => u.Nickname = newNick).ConfigureAwait(false);
|
||||
|
||||
await ReplyErrorLocalizedAsync(strs.bot_nick(Format.Bold(newNick) ?? "-"));
|
||||
await ReplyConfirmLocalizedAsync(strs.bot_nick(Format.Bold(newNick) ?? "-"));
|
||||
}
|
||||
|
||||
[NadekoCommand, Aliases]
|
||||
@@ -395,7 +395,7 @@ namespace NadekoBot.Modules.Administration
|
||||
|
||||
await gu.ModifyAsync(u => u.Nickname = newNick).ConfigureAwait(false);
|
||||
|
||||
await ReplyErrorLocalizedAsync(strs.user_nick(Format.Bold(gu.ToString()), Format.Bold(newNick) ?? "-"));
|
||||
await ReplyConfirmLocalizedAsync(strs.user_nick(Format.Bold(gu.ToString()), Format.Bold(newNick) ?? "-"));
|
||||
}
|
||||
|
||||
[NadekoCommand, Aliases]
|
||||
@@ -496,7 +496,7 @@ namespace NadekoBot.Modules.Administration
|
||||
public async Task ImagesReload()
|
||||
{
|
||||
await _service.ReloadImagesAsync();
|
||||
await ReplyErrorLocalizedAsync(strs.images_loading);
|
||||
await ReplyConfirmLocalizedAsync(strs.images_loading);
|
||||
}
|
||||
|
||||
[NadekoCommand, Aliases]
|
||||
|
@@ -41,6 +41,15 @@ namespace NadekoBot.Modules.Administration
|
||||
await ReplyPendingLocalizedAsync(strs.boostdel_off).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageGuild)]
|
||||
public Task BoostMsg()
|
||||
{
|
||||
var boostMessage = _service.GetBoostMessage(ctx.Guild.Id);
|
||||
return ReplyConfirmLocalizedAsync(strs.boostmsg_cur(boostMessage?.SanitizeMentions()));
|
||||
}
|
||||
|
||||
[NadekoCommand, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageGuild)]
|
||||
@@ -48,7 +57,7 @@ namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
{
|
||||
await GreetMsg().ConfigureAwait(false);
|
||||
await BoostMsg().ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@@ -1,9 +1,7 @@
|
||||
using System;
|
||||
using Discord;
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using Discord.WebSocket;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NadekoBot.Common;
|
||||
using NadekoBot.Common.Collections;
|
||||
using NadekoBot.Common.Replacements;
|
||||
using NadekoBot.Services;
|
||||
@@ -24,14 +22,11 @@ namespace NadekoBot.Modules.Administration.Services
|
||||
|
||||
private readonly DbService _db;
|
||||
private readonly ILogCommandService _logService;
|
||||
private readonly IEmbedBuilderService _eb;
|
||||
|
||||
public AdministrationService(Bot bot, CommandHandler cmdHandler, DbService db,
|
||||
ILogCommandService logService, IEmbedBuilderService eb)
|
||||
public AdministrationService(Bot bot, CommandHandler cmdHandler, DbService db, ILogCommandService logService)
|
||||
{
|
||||
_db = db;
|
||||
_logService = logService;
|
||||
_eb = eb;
|
||||
|
||||
DeleteMessagesOnCommand = new ConcurrentHashSet<ulong>(bot.AllGuildConfigs
|
||||
.Where(g => g.DeleteMessageOnCommand)
|
||||
|
@@ -0,0 +1,187 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Channels;
|
||||
using System.Threading.Tasks;
|
||||
using Discord;
|
||||
using Discord.Net;
|
||||
using Discord.WebSocket;
|
||||
using LinqToDB;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using NadekoBot.Common.Collections;
|
||||
using NadekoBot.Common.ModuleBehaviors;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Services;
|
||||
using Serilog;
|
||||
|
||||
namespace NadekoBot.Modules.Administration.Services
|
||||
{
|
||||
public sealed class ImageOnlyChannelService : IEarlyBehavior
|
||||
{
|
||||
private readonly IMemoryCache _ticketCache;
|
||||
private readonly DiscordSocketClient _client;
|
||||
private readonly DbService _db;
|
||||
private readonly ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>> _enabledOn;
|
||||
|
||||
private Channel<IUserMessage> _deleteQueue = Channel.CreateBounded<IUserMessage>(new BoundedChannelOptions(100)
|
||||
{
|
||||
FullMode = BoundedChannelFullMode.DropOldest,
|
||||
SingleReader = true,
|
||||
SingleWriter = false,
|
||||
});
|
||||
|
||||
|
||||
public ImageOnlyChannelService(IMemoryCache ticketCache, DiscordSocketClient client, DbService db)
|
||||
{
|
||||
_ticketCache = ticketCache;
|
||||
_client = client;
|
||||
_db = db;
|
||||
|
||||
var uow = _db.GetDbContext();
|
||||
_enabledOn = uow.ImageOnlyChannels
|
||||
.ToList()
|
||||
.GroupBy(x => x.GuildId)
|
||||
.ToDictionary(x => x.Key, x => new ConcurrentHashSet<ulong>(x.Select(x => x.ChannelId)))
|
||||
.ToConcurrent();
|
||||
|
||||
_ = Task.Run(DeleteQueueRunner);
|
||||
|
||||
_client.ChannelDestroyed += ClientOnChannelDestroyed;
|
||||
}
|
||||
|
||||
private Task ClientOnChannelDestroyed(SocketChannel ch)
|
||||
{
|
||||
if (ch is not IGuildChannel gch)
|
||||
return Task.CompletedTask;
|
||||
|
||||
if (_enabledOn.TryGetValue(gch.GuildId, out var channels) && channels.TryRemove(ch.Id))
|
||||
ToggleImageOnlyChannel(gch.GuildId, ch.Id, true);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task DeleteQueueRunner()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var toDelete = await _deleteQueue.Reader.ReadAsync();
|
||||
try
|
||||
{
|
||||
await toDelete.DeleteAsync();
|
||||
await Task.Delay(1000);
|
||||
}
|
||||
catch (HttpException ex) when (ex.HttpCode == HttpStatusCode.Forbidden)
|
||||
{
|
||||
// disable if bot can't delete messages in the channel
|
||||
ToggleImageOnlyChannel(((ITextChannel)toDelete.Channel).GuildId, toDelete.Channel.Id, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool ToggleImageOnlyChannel(ulong guildId, ulong channelId, bool forceDisable = false)
|
||||
{
|
||||
var newState = false;
|
||||
using var uow = _db.GetDbContext();
|
||||
if (forceDisable
|
||||
|| (_enabledOn.TryGetValue(guildId, out var channels)
|
||||
&& channels.TryRemove(channelId)))
|
||||
{
|
||||
uow.ImageOnlyChannels.Delete(x => x.ChannelId == channelId);
|
||||
}
|
||||
else
|
||||
{
|
||||
uow.ImageOnlyChannels.Add(new()
|
||||
{
|
||||
GuildId = guildId,
|
||||
ChannelId = channelId
|
||||
});
|
||||
|
||||
channels = _enabledOn.GetOrAdd(guildId, new ConcurrentHashSet<ulong>());
|
||||
channels.Add(channelId);
|
||||
newState = true;
|
||||
}
|
||||
|
||||
uow.SaveChanges();
|
||||
return newState;
|
||||
}
|
||||
|
||||
public async Task<bool> RunBehavior(IGuild guild, IUserMessage msg)
|
||||
{
|
||||
if (msg.Channel is not ITextChannel tch)
|
||||
return false;
|
||||
|
||||
if (msg.Attachments.Any(x => x is { Height: > 0, Width: > 0 }))
|
||||
return false;
|
||||
|
||||
if (!_enabledOn.TryGetValue(tch.GuildId, out var chs)
|
||||
|| !chs.Contains(msg.Channel.Id))
|
||||
return false;
|
||||
|
||||
var user = await tch.Guild.GetUserAsync(msg.Author.Id)
|
||||
?? await _client.Rest.GetGuildUserAsync(tch.GuildId, msg.Author.Id);
|
||||
|
||||
if (user is null)
|
||||
return false;
|
||||
|
||||
// ignore owner and admin
|
||||
if (user.Id == tch.Guild.OwnerId || user.GuildPermissions.Administrator)
|
||||
{
|
||||
Log.Information("Image-Only: Ignoring owner od admin ({ChannelId})", msg.Channel.Id);
|
||||
return false;
|
||||
}
|
||||
|
||||
// ignore users higher in hierarchy
|
||||
var botUser = await tch.Guild.GetCurrentUserAsync();
|
||||
if (user.GetRoles().Max(x => x.Position) >= botUser.GetRoles().Max(x => x.Position))
|
||||
return false;
|
||||
|
||||
// can't modify channel perms if not admin apparently
|
||||
if (!botUser.GuildPermissions.ManageGuild)
|
||||
{
|
||||
ToggleImageOnlyChannel( tch.GuildId, tch.Id, true);;
|
||||
return false;
|
||||
}
|
||||
|
||||
var shouldLock = AddUserTicket(tch.GuildId, msg.Author.Id);
|
||||
if (shouldLock)
|
||||
{
|
||||
await tch.AddPermissionOverwriteAsync(msg.Author, new(sendMessages: PermValue.Deny));
|
||||
Log.Warning("Image-Only: User {User} [{UserId}] has been banned from typing in the channel [{ChannelId}]",
|
||||
msg.Author,
|
||||
msg.Author.Id,
|
||||
msg.Channel.Id);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await _deleteQueue.Writer.WriteAsync(msg);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Error deleting message {MessageId} in image-only channel {ChannelId}.",
|
||||
msg.Id,
|
||||
tch.Id);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool AddUserTicket(ulong guildId, ulong userId)
|
||||
{
|
||||
var old = _ticketCache.GetOrCreate($"{guildId}_{userId}", entry =>
|
||||
{
|
||||
entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromDays(1);
|
||||
return 0;
|
||||
});
|
||||
|
||||
_ticketCache.Set($"{guildId}_{userId}", ++old);
|
||||
|
||||
// if this is the third time that the user posts a
|
||||
// non image in an image-only channel on this server
|
||||
return old > 2;
|
||||
}
|
||||
|
||||
public int Priority { get; } = 0;
|
||||
}
|
||||
}
|
@@ -123,7 +123,7 @@ namespace NadekoBot.Modules.Administration.Services
|
||||
break;
|
||||
case PunishmentAction.Ban:
|
||||
if (minutes == 0)
|
||||
await guild.AddBanAsync(user, reason: reason).ConfigureAwait(false);
|
||||
await guild.AddBanAsync(user, reason: reason, pruneDays: 7).ConfigureAwait(false);
|
||||
else
|
||||
await _mute.TimedBan(user.Guild, user, TimeSpan.FromMinutes(minutes), reason)
|
||||
.ConfigureAwait(false);
|
||||
|
@@ -9,9 +9,11 @@ using NadekoBot.Services.Database.Models;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Modules.Administration.Services;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using NadekoBot.Modules.Permissions.Services;
|
||||
using NadekoBot.Modules.Searches.Common;
|
||||
using Serilog;
|
||||
|
||||
namespace NadekoBot.Modules.Administration
|
||||
@@ -762,6 +764,78 @@ namespace NadekoBot.Modules.Administration
|
||||
await ctx.Channel.EmbedAsync(toSend)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.BanMembers)]
|
||||
[BotPerm(GuildPerm.BanMembers)]
|
||||
[Ratelimit(30)]
|
||||
public async Task MassBan(params string[] userStrings)
|
||||
{
|
||||
if (userStrings.Length == 0)
|
||||
return;
|
||||
|
||||
var missing = new List<string>();
|
||||
var banning = new HashSet<IGuildUser>();
|
||||
|
||||
await ctx.Channel.TriggerTypingAsync();
|
||||
foreach (var userStr in userStrings)
|
||||
{
|
||||
if (ulong.TryParse(userStr, out var userId))
|
||||
{
|
||||
var user = await ctx.Guild.GetUserAsync(userId) ??
|
||||
await ((DiscordSocketClient)Context.Client).Rest.GetGuildUserAsync(ctx.Guild.Id, userId);
|
||||
|
||||
if (user is null)
|
||||
{
|
||||
missing.Add(userStr);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!await CheckRoleHierarchy(user))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
banning.Add(user);
|
||||
}
|
||||
else
|
||||
{
|
||||
missing.Add(userStr);
|
||||
}
|
||||
}
|
||||
|
||||
var missStr = string.Join("\n", missing);
|
||||
if (string.IsNullOrWhiteSpace(missStr))
|
||||
missStr = "-";
|
||||
|
||||
var toSend = _eb.Create(ctx)
|
||||
.WithDescription(GetText(strs.mass_ban_in_progress(banning.Count)))
|
||||
.AddField(GetText(strs.invalid(missing.Count)), missStr)
|
||||
.WithPendingColor();
|
||||
|
||||
var banningMessage = await ctx.Channel.EmbedAsync(toSend);
|
||||
|
||||
foreach (var toBan in banning)
|
||||
{
|
||||
try
|
||||
{
|
||||
await toBan.BanAsync(7);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warning(ex, "Error banning {User} user in {GuildId} server",
|
||||
toBan.Id,
|
||||
ctx.Guild.Id);
|
||||
}
|
||||
}
|
||||
|
||||
await banningMessage.ModifyAsync(x => x.Embed = _eb.Create()
|
||||
.WithDescription(GetText(strs.mass_ban_completed(banning.Count())))
|
||||
.AddField(GetText(strs.invalid(missing.Count)), missStr)
|
||||
.WithOkColor()
|
||||
.Build()).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
@@ -783,7 +857,7 @@ namespace NadekoBot.Modules.Administration
|
||||
var banningMessageTask = ctx.Channel.EmbedAsync(_eb.Create()
|
||||
.WithDescription(GetText(strs.mass_kill_in_progress(bans.Count())))
|
||||
.AddField(GetText(strs.invalid(missing)), missStr)
|
||||
.WithOkColor());
|
||||
.WithPendingColor());
|
||||
|
||||
//do the banning
|
||||
await Task.WhenAll(bans
|
||||
|
@@ -294,7 +294,7 @@ namespace NadekoBot.Modules.CustomReactions
|
||||
.WithDescription("This will delete all custom reactions on this server.")).ConfigureAwait(false))
|
||||
{
|
||||
var count = _service.DeleteAllCustomReactions(ctx.Guild.Id);
|
||||
await ReplyErrorLocalizedAsync(strs.cleared(count));
|
||||
await ReplyConfirmLocalizedAsync(strs.cleared(count));
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -217,7 +217,7 @@ namespace NadekoBot.Modules.Gambling
|
||||
[Priority(0)]
|
||||
public async Task Cash(ulong userId)
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.has(Format.Code(userId.ToString()), $"{GetCurrency(userId)} {CurrencySign}"));
|
||||
await ReplyConfirmLocalizedAsync(strs.has(Format.Code(userId.ToString()), $"{GetCurrency(userId)} {CurrencySign}"));
|
||||
}
|
||||
|
||||
[NadekoCommand, Aliases]
|
||||
@@ -269,7 +269,7 @@ namespace NadekoBot.Modules.Gambling
|
||||
$"Awarded by bot owner. ({ctx.User.Username}/{ctx.User.Id}) {(msg ?? "")}",
|
||||
amount,
|
||||
gamble: (ctx.Client.CurrentUser.Id != usrId)).ConfigureAwait(false);
|
||||
await ReplyErrorLocalizedAsync(strs.awarded(n(amount) + CurrencySign, $"<@{usrId}>"));
|
||||
await ReplyConfirmLocalizedAsync(strs.awarded(n(amount) + CurrencySign, $"<@{usrId}>"));
|
||||
}
|
||||
|
||||
[NadekoCommand, Aliases]
|
||||
@@ -340,7 +340,7 @@ namespace NadekoBot.Modules.Gambling
|
||||
|
||||
if (await _cs.RemoveAsync(usrId, $"Taken by bot owner.({ctx.User.Username}/{ctx.User.Id})", amount,
|
||||
gamble: (ctx.Client.CurrentUser.Id != usrId)).ConfigureAwait(false))
|
||||
await ReplyErrorLocalizedAsync(strs.take(amount + CurrencySign, $"<@{usrId}>"));
|
||||
await ReplyConfirmLocalizedAsync(strs.take(amount + CurrencySign, $"<@{usrId}>"));
|
||||
else
|
||||
await ReplyErrorLocalizedAsync(strs.take_fail(amount + CurrencySign, Format.Code(usrId.ToString()), CurrencySign));
|
||||
}
|
||||
|
@@ -145,11 +145,11 @@ namespace NadekoBot.Modules.Gambling
|
||||
|
||||
if (result == DivorceResult.SucessWithPenalty)
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.waifu_divorced_like(Format.Bold(w.Waifu.ToString()), amount + CurrencySign));
|
||||
await ReplyConfirmLocalizedAsync(strs.waifu_divorced_like(Format.Bold(w.Waifu.ToString()), amount + CurrencySign));
|
||||
}
|
||||
else if (result == DivorceResult.Success)
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.waifu_divorced_notlike(amount + CurrencySign));
|
||||
await ReplyConfirmLocalizedAsync(strs.waifu_divorced_notlike(amount + CurrencySign));
|
||||
}
|
||||
else if (result == DivorceResult.NotYourWife)
|
||||
{
|
||||
@@ -306,7 +306,7 @@ namespace NadekoBot.Modules.Gambling
|
||||
[Priority(1)]
|
||||
public async Task WaifuGift(int page = 1)
|
||||
{
|
||||
if (--page < 0 || page > 3)
|
||||
if (--page < 0 || page > (_config.Waifu.Items.Count - 1) / 9)
|
||||
return;
|
||||
|
||||
var waifuItems = _service.GetWaifuItems();
|
||||
|
@@ -8,6 +8,15 @@ namespace NadekoBot.Modules.Games.Common
|
||||
[Cloneable]
|
||||
public sealed partial class GamesConfig : ICloneable<GamesConfig>
|
||||
{
|
||||
[Comment("DO NOT CHANGE")]
|
||||
public int Version { get; set; }
|
||||
|
||||
[Comment("Hangman related settings (.hangman command)")]
|
||||
public HangmanConfig Hangman { get; set; } = new HangmanConfig()
|
||||
{
|
||||
CurrencyReward = 0
|
||||
};
|
||||
|
||||
[Comment("Trivia related settings (.t command)")]
|
||||
public TriviaConfig Trivia { get; set; } = new TriviaConfig()
|
||||
{
|
||||
@@ -57,6 +66,13 @@ namespace NadekoBot.Modules.Games.Common
|
||||
};
|
||||
}
|
||||
|
||||
[Cloneable]
|
||||
public sealed partial class HangmanConfig
|
||||
{
|
||||
[Comment("The amount of currency awarded to the winner of a hangman game")]
|
||||
public long CurrencyReward { get; set; }
|
||||
}
|
||||
|
||||
[Cloneable]
|
||||
public sealed partial class TriviaConfig
|
||||
{
|
||||
|
@@ -45,7 +45,7 @@ namespace NadekoBot.Modules.Games.Common.Hangman
|
||||
public uint Errors { get; private set; } = 0;
|
||||
public uint MaxErrors { get; } = 6;
|
||||
|
||||
public event Func<Hangman, string, Task> OnGameEnded = delegate { return Task.CompletedTask; };
|
||||
public event Func<Hangman, string, ulong, Task> OnGameEnded = delegate { return Task.CompletedTask; };
|
||||
public event Func<Hangman, string, char, Task> OnLetterAlreadyUsed = delegate { return Task.CompletedTask; };
|
||||
public event Func<Hangman, string, char, Task> OnGuessFailed = delegate { return Task.CompletedTask; };
|
||||
public event Func<Hangman, string, char, Task> OnGuessSucceeded = delegate { return Task.CompletedTask; };
|
||||
@@ -68,7 +68,7 @@ namespace NadekoBot.Modules.Games.Common.Hangman
|
||||
{
|
||||
if (++Errors > MaxErrors)
|
||||
{
|
||||
var _ = OnGameEnded(this, null);
|
||||
var _ = OnGameEnded(this, null, 0);
|
||||
CurrentPhase = Phase.Ended;
|
||||
}
|
||||
}
|
||||
@@ -102,7 +102,7 @@ namespace NadekoBot.Modules.Games.Common.Hangman
|
||||
if (input != Term.Word) // failed
|
||||
return;
|
||||
|
||||
var _ = OnGameEnded?.Invoke(this, userName);
|
||||
var _ = OnGameEnded?.Invoke(this, userName, userId);
|
||||
CurrentPhase = Phase.Ended;
|
||||
return;
|
||||
}
|
||||
@@ -127,7 +127,7 @@ namespace NadekoBot.Modules.Games.Common.Hangman
|
||||
else if (Term.Word.All(x => _previousGuesses.IsSupersetOf(Term.Word.ToLowerInvariant()
|
||||
.Where(char.IsLetterOrDigit))))
|
||||
{
|
||||
var _ = OnGameEnded.Invoke(this, userName); // if all letters are guessed
|
||||
var _ = OnGameEnded.Invoke(this, userName, userId); // if all letters are guessed
|
||||
CurrentPhase = Phase.Ended;
|
||||
}
|
||||
else // guessed but not last letter
|
||||
|
@@ -8,6 +8,7 @@ using NadekoBot.Common.Attributes;
|
||||
using NadekoBot.Modules.Games.Common.Hangman;
|
||||
using NadekoBot.Modules.Games.Services;
|
||||
using NadekoBot.Modules.Games.Common.Hangman.Exceptions;
|
||||
using NadekoBot.Services;
|
||||
|
||||
namespace NadekoBot.Modules.Games
|
||||
{
|
||||
@@ -17,10 +18,14 @@ namespace NadekoBot.Modules.Games
|
||||
public class HangmanCommands : NadekoSubmodule<GamesService>
|
||||
{
|
||||
private readonly DiscordSocketClient _client;
|
||||
private readonly ICurrencyService _cs;
|
||||
private readonly GamesConfigService _gcs;
|
||||
|
||||
public HangmanCommands(DiscordSocketClient client)
|
||||
public HangmanCommands(DiscordSocketClient client, ICurrencyService cs, GamesConfigService gcs)
|
||||
{
|
||||
_client = client;
|
||||
_cs = cs;
|
||||
_gcs = gcs;
|
||||
}
|
||||
|
||||
[NadekoCommand, Aliases]
|
||||
@@ -83,7 +88,7 @@ namespace NadekoBot.Modules.Games
|
||||
}
|
||||
}
|
||||
|
||||
Task Hm_OnGameEnded(Hangman game, string winner)
|
||||
Task Hm_OnGameEnded(Hangman game, string winner, ulong userId)
|
||||
{
|
||||
if (winner is null)
|
||||
{
|
||||
@@ -99,6 +104,10 @@ namespace NadekoBot.Modules.Games
|
||||
return ctx.Channel.EmbedAsync(loseEmbed);
|
||||
}
|
||||
|
||||
var reward = _gcs.Data.Hangman.CurrencyReward;
|
||||
if (reward > 0)
|
||||
_cs.AddAsync(userId, "hangman win", reward, true);
|
||||
|
||||
var winEmbed = _eb.Create().WithTitle($"Hangman Game ({game.TermType}) - Ended")
|
||||
.WithDescription(Format.Bold($"{winner} Won."))
|
||||
.AddField("It was", game.Term.GetWord())
|
||||
|
@@ -18,6 +18,25 @@ namespace NadekoBot.Modules.Games.Services
|
||||
ConfigPrinters.ToString, val => val > 0);
|
||||
AddParsedProp("trivia.currency_reward", gs => gs.Trivia.CurrencyReward, long.TryParse,
|
||||
ConfigPrinters.ToString, val => val >= 0);
|
||||
AddParsedProp("hangman.currency_reward", gs => gs.Hangman.CurrencyReward, long.TryParse,
|
||||
ConfigPrinters.ToString, val => val >= 0);
|
||||
|
||||
Migrate();
|
||||
}
|
||||
|
||||
private void Migrate()
|
||||
{
|
||||
if (_data.Version < 1)
|
||||
{
|
||||
ModifyConfig(c =>
|
||||
{
|
||||
c.Version = 1;
|
||||
c.Hangman = new HangmanConfig()
|
||||
{
|
||||
CurrencyReward = 0
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -434,7 +434,7 @@ namespace NadekoBot.Modules.Help
|
||||
[NadekoCommand, Aliases]
|
||||
public async Task Donate()
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.donate(PatreonUrl, PaypalUrl));
|
||||
await ReplyConfirmLocalizedAsync(strs.donate(PatreonUrl, PaypalUrl));
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -44,6 +44,11 @@ namespace NadekoBot.Modules.Help.Services
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(settings.DmHelpText) || settings.DmHelpText == "-")
|
||||
return Task.CompletedTask;
|
||||
|
||||
// only send dm help text if it contains one of the keywords, if they're specified
|
||||
// if they're not, then reply to every DM
|
||||
if (settings.DmHelpTextKeywords.Any() && !settings.DmHelpTextKeywords.Any(k => msg.Content.Contains(k)))
|
||||
return Task.CompletedTask;
|
||||
|
||||
var rep = new ReplacementBuilder()
|
||||
.WithOverride("%prefix%", () => _bss.Data.Prefix)
|
||||
|
@@ -125,7 +125,7 @@ namespace NadekoBot.Modules.Music
|
||||
queuedMessage?.DeleteAfter(10, _logService);
|
||||
if (mp.IsStopped)
|
||||
{
|
||||
var msg = await ReplyErrorLocalizedAsync(strs.queue_stopped(Format.Code(Prefix + "play")));
|
||||
var msg = await ReplyPendingLocalizedAsync(strs.queue_stopped(Format.Code(Prefix + "play")));
|
||||
msg.DeleteAfter(10, _logService);
|
||||
}
|
||||
}
|
||||
@@ -230,7 +230,7 @@ namespace NadekoBot.Modules.Music
|
||||
return;
|
||||
|
||||
await _service.SetVolumeAsync(ctx.Guild.Id, vol);
|
||||
await ReplyErrorLocalizedAsync(strs.volume_set(vol));
|
||||
await ReplyConfirmLocalizedAsync(strs.volume_set(vol));
|
||||
}
|
||||
|
||||
[NadekoCommand, Aliases]
|
||||
|
@@ -189,7 +189,7 @@ namespace NadekoBot.Modules.NSFW
|
||||
return t;
|
||||
});
|
||||
|
||||
await ReplyErrorLocalizedAsync(strs.started(interval));
|
||||
await ReplyConfirmLocalizedAsync(strs.started(interval));
|
||||
}
|
||||
|
||||
[NadekoCommand, Aliases]
|
||||
@@ -229,7 +229,7 @@ namespace NadekoBot.Modules.NSFW
|
||||
return t;
|
||||
});
|
||||
|
||||
await ReplyErrorLocalizedAsync(strs.started(interval));
|
||||
await ReplyConfirmLocalizedAsync(strs.started(interval));
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -363,9 +363,9 @@ namespace NadekoBot.Modules.NSFW
|
||||
var added = _service.ToggleBlacklistedTag(ctx.Guild.Id, tag);
|
||||
|
||||
if (added)
|
||||
await ReplyErrorLocalizedAsync(strs.blacklisted_tag_add(tag));
|
||||
await ReplyPendingLocalizedAsync(strs.blacklisted_tag_add(tag));
|
||||
else
|
||||
await ReplyErrorLocalizedAsync(strs.blacklisted_tag_remove(tag));
|
||||
await ReplyPendingLocalizedAsync(strs.blacklisted_tag_remove(tag));
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -217,7 +217,7 @@ namespace NadekoBot.Modules.Permissions
|
||||
{
|
||||
}
|
||||
}
|
||||
await ReplyErrorLocalizedAsync(strs.perm_out_of_range).ConfigureAwait(false);
|
||||
await ReplyConfirmLocalizedAsync(strs.perm_out_of_range).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Aliases]
|
||||
|
@@ -5,6 +5,7 @@ using NadekoBot.Extensions;
|
||||
using NadekoBot.Modules.Searches.Services;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using Serilog;
|
||||
|
||||
@@ -15,6 +16,25 @@ namespace NadekoBot.Modules.Searches
|
||||
[Group]
|
||||
public class FeedCommands : NadekoSubmodule<FeedsService>
|
||||
{
|
||||
private static readonly Regex YtChannelRegex =
|
||||
new Regex(@"youtube\.com\/(?:c\/|channel\/|user\/)?(?<channelid>[a-zA-Z0-9\-]{1,})");
|
||||
|
||||
[NadekoCommand, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageMessages)]
|
||||
public Task YtUploadNotif(string url, [Leftover] ITextChannel channel = null)
|
||||
{
|
||||
var m = YtChannelRegex.Match(url);
|
||||
if (!m.Success)
|
||||
{
|
||||
return ReplyErrorLocalizedAsync(strs.invalid_input);
|
||||
}
|
||||
|
||||
var channelId = m.Groups["channelid"].Value;
|
||||
|
||||
return Feed("https://www.youtube.com/feeds/videos.xml?channel_id=" + channelId, channel);
|
||||
}
|
||||
|
||||
[NadekoCommand, Aliases]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageMessages)]
|
||||
@@ -46,7 +66,7 @@ namespace NadekoBot.Modules.Searches
|
||||
}
|
||||
}
|
||||
|
||||
await ReplyErrorLocalizedAsync(strs.feed_not_valid).ConfigureAwait(false);
|
||||
await ReplyConfirmLocalizedAsync(strs.feed_not_valid).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Aliases]
|
||||
|
@@ -191,7 +191,7 @@ namespace NadekoBot.Modules.Searches
|
||||
return;
|
||||
}
|
||||
|
||||
await ReplyErrorLocalizedAsync(strs.stream_message_set_all(count));
|
||||
await ReplyConfirmLocalizedAsync(strs.stream_message_set_all(count));
|
||||
}
|
||||
|
||||
[NadekoCommand, Aliases]
|
||||
|
@@ -118,7 +118,7 @@ namespace NadekoBot.Modules.Searches
|
||||
|
||||
_searches.UserLanguages.AddOrUpdate(ucp, langs, (key, val) => langs);
|
||||
|
||||
await ReplyErrorLocalizedAsync(strs.atl_set(from, to));
|
||||
await ReplyConfirmLocalizedAsync(strs.atl_set(from, to));
|
||||
}
|
||||
|
||||
[NadekoCommand, Aliases]
|
||||
|
@@ -37,7 +37,7 @@ namespace NadekoBot.Modules.Utility
|
||||
public async Task AliasesClear()
|
||||
{
|
||||
var count = _service.ClearAliases(ctx.Guild.Id);
|
||||
await ReplyErrorLocalizedAsync(strs.aliases_cleared(count));
|
||||
await ReplyConfirmLocalizedAsync(strs.aliases_cleared(count));
|
||||
}
|
||||
|
||||
[NadekoCommand, Aliases]
|
||||
|
@@ -148,7 +148,7 @@ namespace NadekoBot.Modules.Utility
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.reminder_deleted(index + 1));
|
||||
await ReplyConfirmLocalizedAsync(strs.reminder_deleted(index + 1));
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -183,7 +183,9 @@ namespace NadekoBot.Modules.Utility
|
||||
private string GetRepeaterInfoString(RunningRepeater runner)
|
||||
{
|
||||
var intervalString = Format.Bold(runner.Repeater.Interval.ToPrettyStringHM());
|
||||
var executesIn = runner.NextTime - DateTime.UtcNow;
|
||||
var executesIn = runner.NextTime < DateTime.UtcNow
|
||||
? TimeSpan.Zero
|
||||
: runner.NextTime - DateTime.UtcNow;
|
||||
var executesInString = Format.Bold(executesIn.ToPrettyStringHM());
|
||||
var message = Format.Sanitize(runner.Repeater.Message.TrimTo(50));
|
||||
|
||||
|
@@ -76,7 +76,9 @@ where ((guildid >> 22) % {_creds.TotalShards}) == {_client.ShardId};")
|
||||
// because repeaters might've been modified meanwhile
|
||||
if (timeout > TimeSpan.Zero)
|
||||
{
|
||||
await Task.Delay(timeout);
|
||||
await Task.Delay(timeout > TimeSpan.FromMinutes(1)
|
||||
? TimeSpan.FromMinutes(1)
|
||||
: timeout);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -84,16 +86,17 @@ where ((guildid >> 22) % {_creds.TotalShards}) == {_client.ShardId};")
|
||||
var now = DateTime.UtcNow + TimeSpan.FromSeconds(3);
|
||||
|
||||
var toExecute = new List<RunningRepeater>();
|
||||
while (true)
|
||||
lock (_repeaterQueue)
|
||||
{
|
||||
lock (_repeaterQueue)
|
||||
var current = _repeaterQueue.First;
|
||||
while (true)
|
||||
{
|
||||
var current = _repeaterQueue.First;
|
||||
|
||||
if (current is null || current.Value.NextTime > now)
|
||||
break;
|
||||
|
||||
toExecute.Add(current.Value);
|
||||
_repeaterQueue.RemoveFirst();
|
||||
current = current.Next;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,14 +124,24 @@ where ((guildid >> 22) % {_creds.TotalShards}) == {_client.ShardId};")
|
||||
{
|
||||
if (rep.ErrorCount >= 10)
|
||||
{
|
||||
RemoveFromQueue(rep.Repeater.Id);
|
||||
await RemoveRepeaterInternal(rep.Repeater);
|
||||
return;
|
||||
}
|
||||
|
||||
rep.UpdateNextTime();
|
||||
AddToQueue(rep);
|
||||
|
||||
UpdatePosition(rep);
|
||||
}
|
||||
|
||||
|
||||
private void UpdatePosition(RunningRepeater rep)
|
||||
{
|
||||
lock (_queueLocker)
|
||||
{
|
||||
rep.UpdateNextTime();
|
||||
_repeaterQueue.Remove(rep);
|
||||
AddToQueue(rep);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> TriggerExternal(ulong guildId, int index)
|
||||
{
|
||||
using var uow = _db.GetDbContext();
|
||||
|
@@ -92,5 +92,15 @@ namespace NadekoBot.Modules.Utility.Services
|
||||
var initialIntervalMultiplier = 1 - (triggerCount - Math.Truncate(triggerCount));
|
||||
return DateTime.UtcNow + (Repeater.Interval * initialIntervalMultiplier);
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
return obj is RunningRepeater rr && rr.Repeater.Id == this.Repeater.Id;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return this.Repeater.Id;
|
||||
}
|
||||
}
|
||||
}
|
@@ -31,7 +31,7 @@ namespace NadekoBot.Modules.Xp
|
||||
|
||||
_service.XpReset(ctx.Guild.Id, userId);
|
||||
|
||||
await ReplyErrorLocalizedAsync(strs.reset_user(userId));
|
||||
await ReplyConfirmLocalizedAsync(strs.reset_user(userId));
|
||||
}
|
||||
|
||||
[NadekoCommand, Aliases]
|
||||
|
@@ -30,7 +30,7 @@ namespace NadekoBot.Modules.Xp.Services
|
||||
|
||||
private void Migrate()
|
||||
{
|
||||
if (_data.Version <= 1)
|
||||
if (_data.Version < 2)
|
||||
{
|
||||
ModifyConfig(c =>
|
||||
{
|
||||
|
@@ -128,7 +128,7 @@ namespace NadekoBot.Modules.Xp
|
||||
public async Task XpRoleReward(int level)
|
||||
{
|
||||
_service.ResetRoleReward(ctx.Guild.Id, level);
|
||||
await ReplyErrorLocalizedAsync(strs.xp_role_reward_cleared(level));
|
||||
await ReplyConfirmLocalizedAsync(strs.xp_role_reward_cleared(level));
|
||||
}
|
||||
|
||||
[NadekoCommand, Aliases]
|
||||
|
@@ -172,6 +172,12 @@ namespace NadekoBot.Services
|
||||
return uow.GuildConfigsForId(gid, set => set).ChannelGreetMessageText;
|
||||
}
|
||||
}
|
||||
|
||||
public string GetBoostMessage(ulong gid)
|
||||
{
|
||||
using var uow = _db.GetDbContext();
|
||||
return uow.GuildConfigsForId(gid, set => set).BoostMessage;
|
||||
}
|
||||
|
||||
private Task ByeUsers(GreetSettings conf, ITextChannel channel, IUser user)
|
||||
=> ByeUsers(conf, channel, new[] {user});
|
||||
|
@@ -11,11 +11,12 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using NadekoBot.Common.ModuleBehaviors;
|
||||
using Serilog;
|
||||
|
||||
namespace NadekoBot.Services
|
||||
{
|
||||
public sealed class RedisImagesCache : IImageCache
|
||||
public sealed class RedisImagesCache : IImageCache, IReadyExecutor
|
||||
{
|
||||
private readonly ConnectionMultiplexer _con;
|
||||
private readonly IBotCredentials _creds;
|
||||
@@ -73,6 +74,14 @@ namespace NadekoBot.Services
|
||||
Currency,
|
||||
}
|
||||
|
||||
public async Task OnReadyAsync()
|
||||
{
|
||||
if (await AllKeysExist())
|
||||
return;
|
||||
|
||||
await Reload();
|
||||
}
|
||||
|
||||
public RedisImagesCache(ConnectionMultiplexer con, IBotCredentials creds)
|
||||
{
|
||||
_con = con;
|
||||
|
@@ -5,7 +5,6 @@ using System;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Discord;
|
||||
using Discord.WebSocket;
|
||||
|
||||
namespace NadekoBot.Services
|
||||
@@ -20,12 +19,13 @@ namespace NadekoBot.Services
|
||||
private readonly string _redisKey;
|
||||
private readonly EndPoint _redisEndpoint;
|
||||
|
||||
public RedisCache(ConnectionMultiplexer redis, IBotCredentials creds, DiscordSocketClient client)
|
||||
public RedisCache(ConnectionMultiplexer redis, IBotCredentials creds,
|
||||
IImageCache imageCache, ILocalDataCache dataCache)
|
||||
{
|
||||
Redis = redis;
|
||||
_redisEndpoint = Redis.GetEndPoints().First();
|
||||
LocalImages = new RedisImagesCache(Redis, creds);
|
||||
LocalData = new RedisLocalDataCache(Redis, creds, client.ShardId);
|
||||
LocalImages = imageCache;
|
||||
LocalData = dataCache;
|
||||
_redisKey = creds.RedisKey();
|
||||
}
|
||||
|
||||
|
@@ -7,6 +7,8 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Discord;
|
||||
using Discord.WebSocket;
|
||||
using Serilog;
|
||||
|
||||
namespace NadekoBot.Services
|
||||
@@ -25,56 +27,33 @@ namespace NadekoBot.Services
|
||||
|
||||
public IReadOnlyDictionary<string, SearchPokemon> Pokemons
|
||||
{
|
||||
get
|
||||
{
|
||||
return Get<Dictionary<string, SearchPokemon>>("pokemon_list");
|
||||
}
|
||||
private set
|
||||
{
|
||||
Set("pokemon_list", value);
|
||||
}
|
||||
get => Get<Dictionary<string, SearchPokemon>>("pokemon_list");
|
||||
private set => Set("pokemon_list", value);
|
||||
}
|
||||
|
||||
public IReadOnlyDictionary<string, SearchPokemonAbility> PokemonAbilities
|
||||
{
|
||||
get
|
||||
{
|
||||
return Get<Dictionary<string, SearchPokemonAbility>>("pokemon_abilities");
|
||||
}
|
||||
private set
|
||||
{
|
||||
Set("pokemon_abilities", value);
|
||||
}
|
||||
get => Get<Dictionary<string, SearchPokemonAbility>>("pokemon_abilities");
|
||||
private set => Set("pokemon_abilities", value);
|
||||
}
|
||||
|
||||
public TriviaQuestion[] TriviaQuestions
|
||||
{
|
||||
get
|
||||
{
|
||||
return Get<TriviaQuestion[]>("trivia_questions");
|
||||
}
|
||||
private set
|
||||
{
|
||||
Set("trivia_questions", value);
|
||||
}
|
||||
get => Get<TriviaQuestion[]>("trivia_questions");
|
||||
private set => Set("trivia_questions", value);
|
||||
}
|
||||
|
||||
public IReadOnlyDictionary<int, string> PokemonMap
|
||||
{
|
||||
get
|
||||
{
|
||||
return Get<Dictionary<int, string>>("pokemon_map");
|
||||
}
|
||||
private set
|
||||
{
|
||||
Set("pokemon_map", value);
|
||||
}
|
||||
get => Get<Dictionary<int, string>>("pokemon_map");
|
||||
private set => Set("pokemon_map", value);
|
||||
}
|
||||
|
||||
public RedisLocalDataCache(ConnectionMultiplexer con, IBotCredentials creds, int shardId)
|
||||
public RedisLocalDataCache(ConnectionMultiplexer con, IBotCredentials creds, DiscordSocketClient client)
|
||||
{
|
||||
_con = con;
|
||||
_creds = creds;
|
||||
var shardId = client.ShardId;
|
||||
|
||||
if (shardId == 0)
|
||||
{
|
||||
|
@@ -19,7 +19,7 @@ namespace NadekoBot.Services
|
||||
private readonly IBotCredentials _creds;
|
||||
private readonly DateTime _started;
|
||||
|
||||
public const string BotVersion = "3.0.2";
|
||||
public const string BotVersion = "3.0.5";
|
||||
public string Author => "Kwoth#2452";
|
||||
public string Library => "Discord.Net";
|
||||
|
||||
|
@@ -28,19 +28,15 @@ namespace NadekoBot.Services
|
||||
AddParsedProp("locale", bs => bs.DefaultLocale, ConfigParsers.Culture, ConfigPrinters.Culture);
|
||||
AddParsedProp("prefix", bs => bs.Prefix, ConfigParsers.String, ConfigPrinters.ToString);
|
||||
|
||||
UpdateColors();
|
||||
Migrate();
|
||||
}
|
||||
|
||||
private void UpdateColors()
|
||||
private void Migrate()
|
||||
{
|
||||
var ok = _data.Color.Ok;
|
||||
var error = _data.Color.Error;
|
||||
var pend = _data.Color.Pending;
|
||||
}
|
||||
|
||||
protected override void OnStateUpdate()
|
||||
{
|
||||
UpdateColors();
|
||||
if (_data.Version < 2)
|
||||
{
|
||||
ModifyConfig(c => c.Version = 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1140,6 +1140,9 @@ autobutts:
|
||||
eightball:
|
||||
- eightball
|
||||
- 8ball
|
||||
ytuploadnotif:
|
||||
- ytuploadnotif
|
||||
- yun
|
||||
feed:
|
||||
- feed
|
||||
- feedadd
|
||||
@@ -1193,6 +1196,8 @@ crypto:
|
||||
rolelevelreq:
|
||||
- rolelevelreq
|
||||
- rlr
|
||||
massban:
|
||||
- massban
|
||||
masskill:
|
||||
- masskill
|
||||
pathofexile:
|
||||
@@ -1249,4 +1254,8 @@ crsimport:
|
||||
- eximport
|
||||
crsexport:
|
||||
- crsexport
|
||||
- exexport
|
||||
- exexport
|
||||
imageonlychannel:
|
||||
- imageonlychannel
|
||||
- imageonly
|
||||
- imagesonly
|
||||
|
@@ -1,5 +1,5 @@
|
||||
# DO NOT CHANGE
|
||||
version: 1
|
||||
version: 2
|
||||
# Most commands, when executed, have a small colored line
|
||||
# next to the response. The color depends whether the command
|
||||
# is completed, errored or in progress (pending)
|
||||
@@ -28,6 +28,15 @@ forwardToAllOwners: false
|
||||
# Supports embeds. How it looks: https://puu.sh/B0BLV.png
|
||||
dmHelpText: |-
|
||||
{"description": "Type `%prefix%h` for help."}
|
||||
# Only users who send a DM to the bot containing one of the specified words will get a DmHelpText response.
|
||||
# Case insensitive.
|
||||
# Leave empty to reply with DmHelpText to every DM.
|
||||
dmHelpTextKeywords:
|
||||
- help
|
||||
- commands
|
||||
- cmds
|
||||
- module
|
||||
- can you do
|
||||
# This is the response for the .h command
|
||||
helpText: |-
|
||||
{
|
||||
|
@@ -1,3 +1,9 @@
|
||||
# DO NOT CHANGE
|
||||
version: 1
|
||||
# Hangman related settings (.hangman command)
|
||||
hangman:
|
||||
# The amount of currency awarded to the winner of a hangman game
|
||||
currencyReward: 0
|
||||
# Trivia related settings (.t command)
|
||||
trivia:
|
||||
# The amount of currency awarded to the winner of the trivia game.
|
||||
|
@@ -1912,6 +1912,12 @@ eightball:
|
||||
desc: "Ask the 8ball a yes/no question."
|
||||
args:
|
||||
- "Is b1nzy a nice guy?"
|
||||
ytuploadnotif:
|
||||
desc: |-
|
||||
Subscribe to a youtube channel's upload rss feed.
|
||||
Shortcut for `.feed https://www.youtube.com/feeds/videos.xml?channel_id=%3Cyoutube_channel_id`
|
||||
args:
|
||||
- "https://www.youtube.com/channel/UCSJ4gkVC6NrvII8umztf0Ow"
|
||||
feed:
|
||||
desc: "Subscribes to a feed. Bot will post an update up to once every 10 seconds. You can have up to 10 feeds on one server. All feeds must have unique URLs. Set a channel as a second optional parameter to specify where to send the updates."
|
||||
args:
|
||||
@@ -2010,6 +2016,10 @@ rolelevelreq:
|
||||
desc: "Set a level requirement on a self-assignable role."
|
||||
args:
|
||||
- "5 SomeRole"
|
||||
massban:
|
||||
desc: "Bans multiple users at once. Specify a space separated list of IDs of users who you wish to ban."
|
||||
args:
|
||||
- "123123123 3333333333 444444444"
|
||||
masskill:
|
||||
desc: "Specify a new-line separated list of `userid reason`. You can use Username#discrim instead of UserId. Specified users will be banned from the current server, blacklisted from the bot, and have all of their flowers taken away."
|
||||
args:
|
||||
@@ -2104,3 +2114,9 @@ nhentai:
|
||||
args:
|
||||
- "273426"
|
||||
- "cute girl"
|
||||
imageonlychannel:
|
||||
desc: |-
|
||||
Toggles whether the channel only allows images.
|
||||
Users who send more than a few non-image messages will be banned from using the channel.
|
||||
args:
|
||||
- ""
|
@@ -889,6 +889,8 @@
|
||||
"self_assign_not_level": "That self-assignable role requires at least server level {0}.",
|
||||
"invalid": "Invalid / Can't be found ({0})",
|
||||
"mass_kill_in_progress": "Mass Banning and Blacklisting of {0} users is in progress...",
|
||||
"mass_ban_in_progress": "Banning {0} users...",
|
||||
"mass_ban_completed": "Banned {0} users.",
|
||||
"mass_kill_completed": "Mass Banning and Blacklisting of {0} users is complete.",
|
||||
"failed_finding_novel": "Can't find that novel. Make sure you've typed the exact full name, and that it exists on novelupdates.com",
|
||||
"club_transfered": "Ownership of the club {0} has been transferred to {1}",
|
||||
@@ -960,5 +962,7 @@
|
||||
"empty_page": "This page is empty.",
|
||||
"pages": "Pages",
|
||||
"favorites": "Favorites",
|
||||
"tags": "Tags"
|
||||
"tags": "Tags",
|
||||
"imageonly_enable": "This channel is now image-only.",
|
||||
"imageonly_disable": "This channel is no longer image-only."
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user