WHEW. Added placeholders in embeds and quotes, added docs about it to explained features. Wrote a placeholder system and fixed some bugs
This commit is contained in:
parent
aa5b7f96c7
commit
942f15cf05
@ -36,14 +36,4 @@ For example:
|
||||
Now if you try to trigger `/o/`, it won't print anything.
|
||||
|
||||
###Placeholders!
|
||||
There are currently three different placeholders which we will look at, with more placeholders potentially coming in the future.
|
||||
|
||||
| Placeholder | Description | Example Usage | Usage |
|
||||
|:-----------:|-------------|---------------|-------|
|
||||
|`%mention%`|The `%mention%` placeholder is triggered when you type `@BotName` - It's important to note that if you've given the bot a custom nickname, this trigger won't work!|```.acr "Hello %mention%" I, %mention%, also say hello!```|Input: "Hello @BotName" Output: "I, @BotName, also say hello!"|
|
||||
|`%user%`|The `%user%` placeholder mentions the person who said the command|`.acr "Who am I?" You are %user%!`|Input: "Who am I?" Output: "You are @Username!"|
|
||||
|`%rng%`|The `%rng%` placeholder generates a random number between 0 and 10. You can also specify a custom range (%rng1-100%) even with negative numbers: `%rng-9--1%` (from -9 to -1) . |`.acr "Random number" %rng%`|Input: "Random number" Output: "2"|
|
||||
|`%rnduser%`|The `%rnduser%` placeholder mentions a random user from the server. |`.acr "Random user" %rnduser%`|Input: "Random number" Output: @SomeUser|
|
||||
|`%target%`|The `%target%` placeholder is used to make Nadeko Mention another person or phrase, it is only supported as part of the response|`.acr "Say this: " %target%`|Input: "Say this: I, @BotName, am a parrot!". Output: "I, @BotName, am a parrot!".|
|
||||
|
||||
Thanks to Nekai for being creative. <3
|
||||
To learn about placeholders, go [here](Placeholders.md)
|
||||
|
24
docs/Placeholders.md
Normal file
24
docs/Placeholders.md
Normal file
@ -0,0 +1,24 @@
|
||||
Placeholders are used in Quotes, Custom Reactions, Greet/bye messages, playing statuses, and a few other places.
|
||||
|
||||
They can be used to make the message more user friendly, generate random numbers or pictures, etc...
|
||||
|
||||
Some features have their own specific placeholders which are noted in that feature's command help. Some placeholders are not available in certain features because they don't make sense there.
|
||||
|
||||
### Here is a list of the usual placeholders:
|
||||
- `%mention%` - Mention the bot
|
||||
- `%shardid%` - Shard id
|
||||
- `%server%` - Server name
|
||||
- `%sid%` - Server Id
|
||||
- `%channel%` - Channel mention
|
||||
- `%chname%` - Channel mention
|
||||
- `%cid%` - Channel Id
|
||||
- `%user%` - User mention
|
||||
- `%id%` or `%uid%` - User Id
|
||||
- `%userfull%` - Username#discriminator
|
||||
- `%userdiscrim%` - discriminator (for example 1234)
|
||||
- `%rngX-Y%` - Replace X and Y with the range (for example `%rng5-10%` - random between 5 and 10)
|
||||
- `%time%` - Bot time
|
||||
|
||||
**If you're using placeholders in embeds, don't use %user% and in titles, footers and field names. They will not show properly.**
|
||||
|
||||
![img](http://i.imgur.com/lNNNfs1.png)
|
@ -28,6 +28,7 @@ If you want to contribute, be sure to PR on the **[1.4][1.4]** branch.
|
||||
- [Permissions System](Permissions System.md)
|
||||
- [JSON Explanations](JSON Explanations.md)
|
||||
- [Custom Reactions](Custom Reactions.md)
|
||||
- [Placeholders](Placeholders.md)
|
||||
- [Frequently Asked Questions](Frequently Asked Questions.md)
|
||||
- [Contribution Guide](Contribution Guide.md)
|
||||
- [Donate](Donate.md)
|
||||
|
@ -14,9 +14,10 @@ pages:
|
||||
- Readme: Readme.md
|
||||
- Commands List: Commands List.md
|
||||
- Features Explained:
|
||||
- Permissions System: Permissions System.md
|
||||
- JSON Explanations: JSON Explanations.md
|
||||
- Permissions System: Permissions System.md
|
||||
- Custom Reactions: Custom Reactions.md
|
||||
- Placeholders: Placeholders.md
|
||||
- Frequently Asked Questions: Frequently Asked Questions.md
|
||||
- Contribution Guide: Contribution Guide.md
|
||||
- ❤ Donate ❤: Donate.md
|
||||
|
132
src/NadekoBot/DataStructures/Replacements/ReplacementBuilder.cs
Normal file
132
src/NadekoBot/DataStructures/Replacements/ReplacementBuilder.cs
Normal file
@ -0,0 +1,132 @@
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using Discord.WebSocket;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Services;
|
||||
using NadekoBot.Services.Music;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace NadekoBot.DataStructures.Replacements
|
||||
{
|
||||
public class ReplacementBuilder
|
||||
{
|
||||
private static readonly Regex rngRegex = new Regex("%rng(?:(?<from>(?:-)?\\d+)-(?<to>(?:-)?\\d+))?%", RegexOptions.Compiled);
|
||||
private ConcurrentDictionary<string, Func<string>> _reps = new ConcurrentDictionary<string, Func<string>>();
|
||||
private ConcurrentDictionary<Regex, Func<Match, string>> _regex = new ConcurrentDictionary<Regex, Func<Match, string>>();
|
||||
|
||||
public ReplacementBuilder()
|
||||
{
|
||||
WithRngRegex();
|
||||
}
|
||||
|
||||
public ReplacementBuilder WithDefault(IUser usr, IMessageChannel ch, IGuild g, DiscordSocketClient client)
|
||||
{
|
||||
return this.WithUser(usr)
|
||||
.WithChannel(ch)
|
||||
.WithServer(g)
|
||||
.WithClient(client);
|
||||
}
|
||||
|
||||
public ReplacementBuilder WithDefault(ICommandContext ctx) =>
|
||||
WithDefault(ctx.User, ctx.Channel, ctx.Guild, (DiscordSocketClient)ctx.Client);
|
||||
|
||||
public ReplacementBuilder WithClient(DiscordSocketClient client)
|
||||
{
|
||||
_reps.TryAdd("%mention%", () => $"<@{client.CurrentUser.Id}>");
|
||||
_reps.TryAdd("%shardid%", () => client.ShardId.ToString());
|
||||
_reps.TryAdd("%time%", () => DateTime.Now.ToString("HH:mm " + TimeZoneInfo.Local.StandardName.GetInitials()));
|
||||
return this;
|
||||
}
|
||||
|
||||
public ReplacementBuilder WithServer(IGuild g)
|
||||
{
|
||||
_reps.TryAdd("%sid%", () => g == null ? "DM" : g.Id.ToString());
|
||||
_reps.TryAdd("%server%", () => g == null ? "DM" : g.Name);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ReplacementBuilder WithChannel(IMessageChannel ch)
|
||||
{
|
||||
_reps.TryAdd("%channel%", () => (ch as ITextChannel)?.Mention ?? "#" + ch.Name);
|
||||
_reps.TryAdd("%chname%", () => ch.Name);
|
||||
_reps.TryAdd("%cid%", () => ch?.Id.ToString());
|
||||
return this;
|
||||
}
|
||||
|
||||
public ReplacementBuilder WithUser(IUser user)
|
||||
{
|
||||
_reps.TryAdd("%user%", () => user.Mention);
|
||||
_reps.TryAdd("%userfull%", () => user.ToString());
|
||||
_reps.TryAdd("%username%", () => user.Username);
|
||||
_reps.TryAdd("%userdiscrim%", () => user.Discriminator);
|
||||
_reps.TryAdd("%id%", () => user.Id.ToString());
|
||||
_reps.TryAdd("%uid%", () => user.Id.ToString());
|
||||
return this;
|
||||
}
|
||||
|
||||
public ReplacementBuilder WithStats(DiscordSocketClient c)
|
||||
{
|
||||
_reps.TryAdd("%servers%", () => c.Guilds.Count.ToString());
|
||||
_reps.TryAdd("%users%", () => c.Guilds.Sum(s => s.Users.Count).ToString());
|
||||
return this;
|
||||
}
|
||||
|
||||
public ReplacementBuilder WithMusic(MusicService ms)
|
||||
{
|
||||
_reps.TryAdd("%playing%", () =>
|
||||
{
|
||||
var cnt = ms.MusicPlayers.Count(kvp => kvp.Value.CurrentSong != null);
|
||||
if (cnt != 1) return cnt.ToString();
|
||||
try
|
||||
{
|
||||
var mp = ms.MusicPlayers.FirstOrDefault();
|
||||
return mp.Value.CurrentSong.SongInfo.Title;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return "No songs";
|
||||
}
|
||||
});
|
||||
_reps.TryAdd("%queued%", () => ms.MusicPlayers.Sum(kvp => kvp.Value.Playlist.Count).ToString());
|
||||
return this;
|
||||
}
|
||||
|
||||
public ReplacementBuilder WithRngRegex()
|
||||
{
|
||||
var rng = new NadekoRandom();
|
||||
_regex.TryAdd(rngRegex, (match) =>
|
||||
{
|
||||
int from = 0;
|
||||
int.TryParse(match.Groups["from"].ToString(), out from);
|
||||
|
||||
int to = 0;
|
||||
int.TryParse(match.Groups["to"].ToString(), out to);
|
||||
|
||||
if (from == 0 && to == 0)
|
||||
{
|
||||
return rng.Next(0, 11).ToString();
|
||||
}
|
||||
|
||||
if (from >= to)
|
||||
return string.Empty;
|
||||
|
||||
return rng.Next(from, to + 1).ToString();
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
public ReplacementBuilder WithOverride(string key, Func<string> output)
|
||||
{
|
||||
_reps.AddOrUpdate(key, output, delegate { return output; });
|
||||
return this;
|
||||
}
|
||||
|
||||
public Replacer Build()
|
||||
{
|
||||
return new Replacer(_reps.Select(x => (x.Key, x.Value)).ToArray(), _regex.Select(x => (x.Key, x.Value)).ToArray());
|
||||
}
|
||||
}
|
||||
}
|
54
src/NadekoBot/DataStructures/Replacements/Replacer.cs
Normal file
54
src/NadekoBot/DataStructures/Replacements/Replacer.cs
Normal file
@ -0,0 +1,54 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace NadekoBot.DataStructures.Replacements
|
||||
{
|
||||
public class Replacer
|
||||
{
|
||||
private readonly IEnumerable<(string Key, Func<string> Text)> _replacements;
|
||||
private readonly IEnumerable<(Regex Regex, Func<Match, string> Replacement)> _regex;
|
||||
|
||||
public Replacer(IEnumerable<(string, Func<string>)> replacements, IEnumerable<(Regex, Func<Match, string>)> regex)
|
||||
{
|
||||
_replacements = replacements;
|
||||
_regex = regex;
|
||||
}
|
||||
|
||||
public string Replace(string input)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(input))
|
||||
return input;
|
||||
|
||||
foreach (var item in _replacements)
|
||||
{
|
||||
if (input.Contains(item.Key))
|
||||
input = input.Replace(item.Key, item.Text());
|
||||
}
|
||||
|
||||
foreach (var item in _regex)
|
||||
{
|
||||
input = item.Regex.Replace(input, (m) => item.Replacement(m));
|
||||
}
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
public void Replace(CREmbed embedData)
|
||||
{
|
||||
embedData.PlainText = Replace(embedData.PlainText);
|
||||
embedData.Description = Replace(embedData.Description);
|
||||
embedData.Title = Replace(embedData.Title);
|
||||
|
||||
if (embedData.Fields != null)
|
||||
foreach (var f in embedData.Fields)
|
||||
{
|
||||
f.Name = Replace(f.Name);
|
||||
f.Value = Replace(f.Value);
|
||||
}
|
||||
|
||||
if (embedData.Footer != null)
|
||||
embedData.Footer.Text = Replace(embedData.Footer.Text);
|
||||
}
|
||||
}
|
||||
}
|
@ -33,11 +33,11 @@ namespace NadekoBot.Modules.Administration
|
||||
{
|
||||
var config = uow.BotConfig.GetOrCreate();
|
||||
|
||||
_service.RotatingStatuses = config.RotatingStatuses = !config.RotatingStatuses;
|
||||
config.RotatingStatuses = !config.RotatingStatuses;
|
||||
uow.Complete();
|
||||
}
|
||||
}
|
||||
if (_service.RotatingStatuses)
|
||||
if (_service.BotConfig.RotatingStatuses)
|
||||
await ReplyConfirmLocalized("ropl_enabled").ConfigureAwait(false);
|
||||
else
|
||||
await ReplyConfirmLocalized("ropl_disabled").ConfigureAwait(false);
|
||||
@ -52,7 +52,6 @@ namespace NadekoBot.Modules.Administration
|
||||
var config = uow.BotConfig.GetOrCreate();
|
||||
var toAdd = new PlayingStatus { Status = status };
|
||||
config.RotatingStatusMessages.Add(toAdd);
|
||||
_service.RotatingStatusMessages.Add(toAdd);
|
||||
await uow.CompleteAsync();
|
||||
}
|
||||
|
||||
@ -63,13 +62,13 @@ namespace NadekoBot.Modules.Administration
|
||||
[OwnerOnly]
|
||||
public async Task ListPlaying()
|
||||
{
|
||||
if (!_service.RotatingStatusMessages.Any())
|
||||
if (!_service.BotConfig.RotatingStatusMessages.Any())
|
||||
await ReplyErrorLocalized("ropl_not_set").ConfigureAwait(false);
|
||||
else
|
||||
{
|
||||
var i = 1;
|
||||
await ReplyConfirmLocalized("ropl_list",
|
||||
string.Join("\n\t", _service.RotatingStatusMessages.Select(rs => $"`{i++}.` {rs.Status}")))
|
||||
string.Join("\n\t", _service.BotConfig.RotatingStatusMessages.Select(rs => $"`{i++}.` {rs.Status}")))
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@ -90,7 +89,6 @@ namespace NadekoBot.Modules.Administration
|
||||
return;
|
||||
msg = config.RotatingStatusMessages[index].Status;
|
||||
config.RotatingStatusMessages.RemoveAt(index);
|
||||
_service.RotatingStatusMessages.RemoveAt(index);
|
||||
await uow.CompleteAsync();
|
||||
}
|
||||
await ReplyConfirmLocalized("reprm", msg).ConfigureAwait(false);
|
||||
|
@ -9,6 +9,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using NadekoBot.DataStructures;
|
||||
using NadekoBot.DataStructures.Replacements;
|
||||
|
||||
namespace NadekoBot.Modules.Utility
|
||||
{
|
||||
@ -66,19 +67,14 @@ namespace NadekoBot.Modules.Utility
|
||||
if (quote == null)
|
||||
return;
|
||||
|
||||
CREmbed crembed;
|
||||
if (CREmbed.TryParse(quote.Text, out crembed))
|
||||
if (CREmbed.TryParse(quote.Text, out var crembed))
|
||||
{
|
||||
try
|
||||
{
|
||||
await Context.Channel.EmbedAsync(crembed.ToEmbed(), crembed.PlainText ?? "")
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.Warn("Sending CREmbed failed");
|
||||
_log.Warn(ex);
|
||||
}
|
||||
new ReplacementBuilder()
|
||||
.WithDefault(Context)
|
||||
.Build()
|
||||
.Replace(crembed);
|
||||
await Context.Channel.EmbedAsync(crembed.ToEmbed(), crembed.PlainText?.SanitizeMentions() ?? "")
|
||||
.ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
await Context.Channel.SendMessageAsync($"`#{quote.Id}` 📣 " + quote.Text.SanitizeMentions());
|
||||
@ -118,29 +114,27 @@ namespace NadekoBot.Modules.Utility
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
var qfromid = uow.Quotes.Get(id);
|
||||
CREmbed crembed;
|
||||
|
||||
|
||||
if (qfromid == null)
|
||||
{
|
||||
await Context.Channel.SendErrorAsync(GetText("quotes_notfound"));
|
||||
}
|
||||
else if (CREmbed.TryParse(qfromid.Text, out crembed))
|
||||
else if (CREmbed.TryParse(qfromid.Text, out var crembed))
|
||||
{
|
||||
try
|
||||
{
|
||||
await Context.Channel.EmbedAsync(crembed.ToEmbed(), crembed.PlainText ?? "")
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.Warn("Sending CREmbed failed");
|
||||
_log.Warn(ex);
|
||||
}
|
||||
return;
|
||||
new ReplacementBuilder()
|
||||
.WithDefault(Context)
|
||||
.Build()
|
||||
.Replace(crembed);
|
||||
|
||||
await Context.Channel.EmbedAsync(crembed.ToEmbed(), crembed.PlainText?.SanitizeMentions() ?? "")
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
else { await Context.Channel.SendMessageAsync($"`#{qfromid.Id}` 🗯️ " + qfromid.Keyword.ToLowerInvariant().SanitizeMentions() + ": " +
|
||||
qfromid.Text.SanitizeMentions()); }
|
||||
else
|
||||
{
|
||||
await Context.Channel.SendMessageAsync($"`#{qfromid.Id}` 🗯️ " + qfromid.Keyword.ToLowerInvariant().SanitizeMentions() + ": " +
|
||||
qfromid.Text.SanitizeMentions());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -199,7 +199,7 @@ namespace NadekoBot
|
||||
var muteService = new MuteService(Client, AllGuildConfigs, Db);
|
||||
var ratelimitService = new SlowmodeService(Client, AllGuildConfigs);
|
||||
var protectionService = new ProtectionService(Client, AllGuildConfigs, muteService);
|
||||
var playingRotateService = new PlayingRotateService(Client, BotConfig, musicService);
|
||||
var playingRotateService = new PlayingRotateService(Client, BotConfig, musicService, Db);
|
||||
var gameVcService = new GameVoiceChannelService(Client, Db, AllGuildConfigs);
|
||||
var autoAssignRoleService = new AutoAssignRoleService(Client, AllGuildConfigs);
|
||||
var logCommandService = new LogCommandService(Client, Strings, AllGuildConfigs, Db, muteService, protectionService);
|
||||
|
@ -1377,15 +1377,6 @@
|
||||
<data name="typeadd_usage" xml:space="preserve">
|
||||
<value>`{0}typeadd wordswords`</value>
|
||||
</data>
|
||||
<data name="poll_cmd" xml:space="preserve">
|
||||
<value>poll</value>
|
||||
</data>
|
||||
<data name="poll_desc" xml:space="preserve">
|
||||
<value>Creates a poll which requires users to send the number of the voting option to the bot.</value>
|
||||
</data>
|
||||
<data name="poll_usage" xml:space="preserve">
|
||||
<value>`{0}poll Question?;Answer1;Answ 2;A_3`</value>
|
||||
</data>
|
||||
<data name="pollend_cmd" xml:space="preserve">
|
||||
<value>pollend</value>
|
||||
</data>
|
||||
@ -2583,13 +2574,13 @@
|
||||
<data name="togethertube_usage" xml:space="preserve">
|
||||
<value>`{0}totube`</value>
|
||||
</data>
|
||||
<data name="publicpoll_cmd" xml:space="preserve">
|
||||
<value>publicpoll ppoll</value>
|
||||
<data name="poll_cmd" xml:space="preserve">
|
||||
<value>poll ppoll</value>
|
||||
</data>
|
||||
<data name="publicpoll_desc" xml:space="preserve">
|
||||
<data name="poll_desc" xml:space="preserve">
|
||||
<value>Creates a public poll which requires users to type a number of the voting option in the channel command is ran in.</value>
|
||||
</data>
|
||||
<data name="publicpoll_usage" xml:space="preserve">
|
||||
<data name="poll_usage" xml:space="preserve">
|
||||
<value>`{0}ppoll Question?;Answer1;Answ 2;A_3`</value>
|
||||
</data>
|
||||
<data name="autotranslang_cmd" xml:space="preserve">
|
||||
|
@ -1,59 +1,65 @@
|
||||
using Discord.WebSocket;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.DataStructures.Replacements;
|
||||
using NadekoBot.Services;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using NadekoBot.Services.Music;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace NadekoBot.Services.Administration
|
||||
{
|
||||
//todo 99 - Could make a placeholder service, which can work for any module
|
||||
//and have replacements which are dependent on the types provided in the constructor
|
||||
public class PlayingRotateService
|
||||
{
|
||||
public List<PlayingStatus> RotatingStatusMessages { get; }
|
||||
public volatile bool RotatingStatuses;
|
||||
private readonly Timer _t;
|
||||
private readonly DiscordSocketClient _client;
|
||||
private readonly BotConfig _bc;
|
||||
private readonly MusicService _music;
|
||||
private readonly Logger _log;
|
||||
private readonly Replacer _rep;
|
||||
private readonly DbService _db;
|
||||
public BotConfig BotConfig { get; private set; } //todo load whole botconifg, not just for this service when you have the time
|
||||
|
||||
private class TimerState
|
||||
{
|
||||
public int Index { get; set; }
|
||||
}
|
||||
|
||||
public PlayingRotateService(DiscordSocketClient client, BotConfig bc, MusicService music)
|
||||
public PlayingRotateService(DiscordSocketClient client, BotConfig bc, MusicService music, DbService db)
|
||||
{
|
||||
_client = client;
|
||||
_bc = bc;
|
||||
BotConfig = bc;
|
||||
_music = music;
|
||||
_db = db;
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
_rep = new ReplacementBuilder()
|
||||
.WithClient(client)
|
||||
.WithStats(client)
|
||||
.WithMusic(music)
|
||||
.Build();
|
||||
|
||||
RotatingStatusMessages = _bc.RotatingStatusMessages;
|
||||
RotatingStatuses = _bc.RotatingStatuses;
|
||||
|
||||
_t = new Timer(async (objState) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
BotConfig = uow.BotConfig.GetOrCreate();
|
||||
}
|
||||
var state = (TimerState)objState;
|
||||
if (!RotatingStatuses)
|
||||
if (!BotConfig.RotatingStatuses)
|
||||
return;
|
||||
if (state.Index >= RotatingStatusMessages.Count)
|
||||
if (state.Index >= BotConfig.RotatingStatusMessages.Count)
|
||||
state.Index = 0;
|
||||
|
||||
if (!RotatingStatusMessages.Any())
|
||||
if (!BotConfig.RotatingStatusMessages.Any())
|
||||
return;
|
||||
var status = RotatingStatusMessages[state.Index++].Status;
|
||||
var status = BotConfig.RotatingStatusMessages[state.Index++].Status;
|
||||
if (string.IsNullOrWhiteSpace(status))
|
||||
return;
|
||||
PlayingPlaceholders.ForEach(e => status = status.Replace(e.Key, e.Value(_client, _music)));
|
||||
ShardSpecificPlaceholders.ForEach(e => status = status.Replace(e.Key, e.Value(client)));
|
||||
|
||||
status = _rep.Replace(status);
|
||||
|
||||
try { await client.SetGameAsync(status).ConfigureAwait(false); }
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -66,31 +72,5 @@ namespace NadekoBot.Services.Administration
|
||||
}
|
||||
}, new TimerState(), TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1));
|
||||
}
|
||||
|
||||
public Dictionary<string, Func<DiscordSocketClient, MusicService, string>> PlayingPlaceholders { get; } =
|
||||
new Dictionary<string, Func<DiscordSocketClient, MusicService, string>> {
|
||||
{ "%servers%", (c, ms) => c.Guilds.Count.ToString()},
|
||||
{ "%users%", (c, ms) => c.Guilds.Sum(s => s.Users.Count).ToString()},
|
||||
{ "%playing%", (c, ms) => {
|
||||
var cnt = ms.MusicPlayers.Count(kvp => kvp.Value.CurrentSong != null);
|
||||
if (cnt != 1) return cnt.ToString();
|
||||
try {
|
||||
var mp = ms.MusicPlayers.FirstOrDefault();
|
||||
return mp.Value.CurrentSong.SongInfo.Title;
|
||||
}
|
||||
catch {
|
||||
return "No songs";
|
||||
}
|
||||
}
|
||||
},
|
||||
{ "%queued%", (c, ms) => ms.MusicPlayers.Sum(kvp => kvp.Value.Playlist.Count).ToString()},
|
||||
{ "%time%", (c, ms) => DateTime.Now.ToString("HH:mm " + TimeZoneInfo.Local.StandardName.GetInitials()) },
|
||||
};
|
||||
|
||||
public Dictionary<string, Func<DiscordSocketClient, string>> ShardSpecificPlaceholders { get; } =
|
||||
new Dictionary<string, Func<DiscordSocketClient, string>> {
|
||||
{ "%shardid%", (client) => client.ShardId.ToString()},
|
||||
{ "%shardguilds%", (client) => client.Guilds.Count.ToString()},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ using AngleSharp.Dom.Html;
|
||||
using Discord;
|
||||
using Discord.WebSocket;
|
||||
using NadekoBot.DataStructures;
|
||||
using NadekoBot.DataStructures.Replacements;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using System;
|
||||
@ -15,62 +16,12 @@ namespace NadekoBot.Services.CustomReactions
|
||||
{
|
||||
public static class Extensions
|
||||
{
|
||||
public static Dictionary<string, Func<IUserMessage, string, string>> responsePlaceholders = new Dictionary<string, Func<IUserMessage, string, string>>()
|
||||
{
|
||||
{"%target%", (ctx, trigger) => { return ctx.Content.Substring(trigger.Length).Trim().SanitizeMentions(); } },
|
||||
{"%rnduser%", (ctx, client) => {
|
||||
//var ch = ctx.Channel as ITextChannel;
|
||||
//if(ch == null)
|
||||
// return "";
|
||||
|
||||
//var g = ch.Guild as SocketGuild;
|
||||
//if(g == null)
|
||||
// return "";
|
||||
//try {
|
||||
// var usr = g.Users.Skip(new NadekoRandom().Next(0, g.Users.Count)).FirstOrDefault();
|
||||
// return usr.Mention;
|
||||
//}
|
||||
//catch {
|
||||
return "[%rnduser% is temp. disabled]";
|
||||
//}
|
||||
|
||||
//var users = g.Users.ToArray();
|
||||
|
||||
//return users[new NadekoRandom().Next(0, users.Length-1)].Mention;
|
||||
} },
|
||||
};
|
||||
|
||||
public static Dictionary<string, Func<IUserMessage, DiscordSocketClient, string>> placeholders = new Dictionary<string, Func<IUserMessage, DiscordSocketClient, string>>()
|
||||
{
|
||||
{"%mention%", (ctx, client) => { return $"<@{client.CurrentUser.Id}>"; } },
|
||||
{"%user%", (ctx, client) => { return ctx.Author.Mention; } },
|
||||
//{"%rng%", (ctx) => { return new NadekoRandom().Next(0,10).ToString(); } }
|
||||
};
|
||||
|
||||
private static readonly Regex rngRegex = new Regex("%rng(?:(?<from>(?:-)?\\d+)-(?<to>(?:-)?\\d+))?%", RegexOptions.Compiled);
|
||||
private static readonly Regex imgRegex = new Regex("%(img|image):(?<tag>.*?)%", RegexOptions.Compiled);
|
||||
|
||||
private static readonly NadekoRandom rng = new NadekoRandom();
|
||||
|
||||
public static Dictionary<Regex, Func<Match, Task<string>>> regexPlaceholders = new Dictionary<Regex, Func<Match, Task<string>>>()
|
||||
{
|
||||
{ rngRegex, (match) => {
|
||||
int from = 0;
|
||||
int.TryParse(match.Groups["from"].ToString(), out from);
|
||||
|
||||
int to = 0;
|
||||
int.TryParse(match.Groups["to"].ToString(), out to);
|
||||
|
||||
if(from == 0 && to == 0)
|
||||
{
|
||||
return Task.FromResult(rng.Next(0, 11).ToString());
|
||||
}
|
||||
|
||||
if(from >= to)
|
||||
return Task.FromResult(string.Empty);
|
||||
|
||||
return Task.FromResult(rng.Next(from,to+1).ToString());
|
||||
} },
|
||||
{ imgRegex, async (match) => {
|
||||
var tag = match.Groups["tag"].ToString();
|
||||
if(string.IsNullOrWhiteSpace(tag))
|
||||
@ -96,29 +47,24 @@ namespace NadekoBot.Services.CustomReactions
|
||||
|
||||
private static string ResolveTriggerString(this string str, IUserMessage ctx, DiscordSocketClient client)
|
||||
{
|
||||
foreach (var ph in placeholders)
|
||||
{
|
||||
if (str.Contains(ph.Key))
|
||||
str = str.ToLowerInvariant().Replace(ph.Key, ph.Value(ctx, client));
|
||||
}
|
||||
var rep = new ReplacementBuilder()
|
||||
.WithUser(ctx.Author)
|
||||
.WithClient(client)
|
||||
.Build();
|
||||
|
||||
str = rep.Replace(str.ToLowerInvariant());
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
private static async Task<string> ResolveResponseStringAsync(this string str, IUserMessage ctx, DiscordSocketClient client, string resolvedTrigger)
|
||||
{
|
||||
foreach (var ph in placeholders)
|
||||
{
|
||||
var lowerKey = ph.Key.ToLowerInvariant();
|
||||
if (str.Contains(lowerKey))
|
||||
str = str.Replace(lowerKey, ph.Value(ctx, client));
|
||||
}
|
||||
var rep = new ReplacementBuilder()
|
||||
.WithDefault(ctx.Author, ctx.Channel, (ctx.Channel as ITextChannel)?.Guild, client)
|
||||
.WithOverride("%target%", () => ctx.Content.Substring(resolvedTrigger.Length).Trim())
|
||||
.Build();
|
||||
|
||||
foreach (var ph in responsePlaceholders)
|
||||
{
|
||||
var lowerKey = ph.Key.ToLowerInvariant();
|
||||
if (str.Contains(lowerKey))
|
||||
str = str.Replace(lowerKey, ph.Value(ctx, resolvedTrigger));
|
||||
}
|
||||
str = rep.Replace(str.ToLowerInvariant());
|
||||
|
||||
foreach (var ph in regexPlaceholders)
|
||||
{
|
||||
@ -133,17 +79,24 @@ namespace NadekoBot.Services.CustomReactions
|
||||
public static Task<string > ResponseWithContextAsync(this CustomReaction cr, IUserMessage ctx, DiscordSocketClient client)
|
||||
=> cr.Response.ResolveResponseStringAsync(ctx, client, cr.Trigger.ResolveTriggerString(ctx, client));
|
||||
|
||||
public static async Task<IUserMessage> Send(this CustomReaction cr, IUserMessage context, DiscordSocketClient client, CustomReactionsService crs)
|
||||
public static async Task<IUserMessage> Send(this CustomReaction cr, IUserMessage ctx, DiscordSocketClient client, CustomReactionsService crs)
|
||||
{
|
||||
var channel = cr.DmResponse ? await context.Author.CreateDMChannelAsync() : context.Channel;
|
||||
var channel = cr.DmResponse ? await ctx.Author.CreateDMChannelAsync() : ctx.Channel;
|
||||
|
||||
crs.ReactionStats.AddOrUpdate(cr.Trigger, 1, (k, old) => ++old);
|
||||
|
||||
if (CREmbed.TryParse(cr.Response, out CREmbed crembed))
|
||||
{
|
||||
return await channel.EmbedAsync(crembed.ToEmbed(), crembed.PlainText ?? "");
|
||||
var rep = new ReplacementBuilder()
|
||||
.WithDefault(ctx.Author, ctx.Channel, (ctx.Channel as ITextChannel)?.Guild, client)
|
||||
.WithOverride("%target%", () => ctx.Content.Substring(cr.Trigger.ResolveTriggerString(ctx, client).Length).Trim())
|
||||
.Build();
|
||||
|
||||
rep.Replace(crembed);
|
||||
|
||||
return await channel.EmbedAsync(crembed.ToEmbed(), crembed.PlainText?.SanitizeMentions() ?? "");
|
||||
}
|
||||
return await channel.SendMessageAsync((await cr.ResponseWithContextAsync(context, client)).SanitizeMentions());
|
||||
return await channel.SendMessageAsync((await cr.ResponseWithContextAsync(ctx, client)).SanitizeMentions());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
using Discord;
|
||||
using Discord.WebSocket;
|
||||
using NadekoBot.DataStructures;
|
||||
using NadekoBot.DataStructures.Replacements;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using NLog;
|
||||
@ -45,15 +46,17 @@ namespace NadekoBot.Services
|
||||
|
||||
if (channel == null) //maybe warn the server owner that the channel is missing
|
||||
return;
|
||||
CREmbed embedData;
|
||||
if (CREmbed.TryParse(conf.ChannelByeMessageText, out embedData))
|
||||
|
||||
var rep = new ReplacementBuilder()
|
||||
.WithDefault(user, channel, user.Guild, _client)
|
||||
.Build();
|
||||
|
||||
if (CREmbed.TryParse(conf.ChannelByeMessageText, out var embedData))
|
||||
{
|
||||
embedData.PlainText = embedData.PlainText?.Replace("%user%", user.Username).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
|
||||
embedData.Description = embedData.Description?.Replace("%user%", user.Username).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
|
||||
embedData.Title = embedData.Title?.Replace("%user%", user.Username).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
|
||||
rep.Replace(embedData);
|
||||
try
|
||||
{
|
||||
var toDelete = await channel.EmbedAsync(embedData.ToEmbed(), embedData.PlainText ?? "").ConfigureAwait(false);
|
||||
var toDelete = await channel.EmbedAsync(embedData.ToEmbed(), embedData.PlainText?.SanitizeMentions() ?? "").ConfigureAwait(false);
|
||||
if (conf.AutoDeleteByeMessagesTimer > 0)
|
||||
{
|
||||
toDelete.DeleteAfter(conf.AutoDeleteByeMessagesTimer);
|
||||
@ -63,7 +66,7 @@ namespace NadekoBot.Services
|
||||
}
|
||||
else
|
||||
{
|
||||
var msg = conf.ChannelByeMessageText.Replace("%user%", user.Username).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
|
||||
var msg = rep.Replace(conf.ChannelByeMessageText);
|
||||
if (string.IsNullOrWhiteSpace(msg))
|
||||
return;
|
||||
try
|
||||
@ -98,16 +101,16 @@ namespace NadekoBot.Services
|
||||
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 rep = new ReplacementBuilder()
|
||||
.WithDefault(user, channel, user.Guild, _client)
|
||||
.Build();
|
||||
|
||||
CREmbed embedData;
|
||||
if (CREmbed.TryParse(conf.ChannelGreetMessageText, out embedData))
|
||||
if (CREmbed.TryParse(conf.ChannelGreetMessageText, out var embedData))
|
||||
{
|
||||
embedData.PlainText = embedData.PlainText?.Replace("%user%", user.Mention).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
|
||||
embedData.Description = embedData.Description?.Replace("%user%", user.Mention).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
|
||||
embedData.Title = embedData.Title?.Replace("%user%", user.ToString()).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
|
||||
rep.Replace(embedData);
|
||||
try
|
||||
{
|
||||
var toDelete = await channel.EmbedAsync(embedData.ToEmbed(), embedData.PlainText ?? "").ConfigureAwait(false);
|
||||
var toDelete = await channel.EmbedAsync(embedData.ToEmbed(), embedData.PlainText?.SanitizeMentions() ?? "").ConfigureAwait(false);
|
||||
if (conf.AutoDeleteGreetMessagesTimer > 0)
|
||||
{
|
||||
toDelete.DeleteAfter(conf.AutoDeleteGreetMessagesTimer);
|
||||
@ -117,7 +120,7 @@ namespace NadekoBot.Services
|
||||
}
|
||||
else
|
||||
{
|
||||
var msg = conf.ChannelGreetMessageText.Replace("%user%", user.Mention).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
|
||||
var msg = rep.Replace(conf.ChannelGreetMessageText);
|
||||
if (!string.IsNullOrWhiteSpace(msg))
|
||||
{
|
||||
try
|
||||
@ -140,21 +143,22 @@ namespace NadekoBot.Services
|
||||
|
||||
if (channel != null)
|
||||
{
|
||||
CREmbed embedData;
|
||||
if (CREmbed.TryParse(conf.DmGreetMessageText, out embedData))
|
||||
var rep = new ReplacementBuilder()
|
||||
.WithDefault(user, channel, user.Guild, _client)
|
||||
.Build();
|
||||
if (CREmbed.TryParse(conf.DmGreetMessageText, out var embedData))
|
||||
{
|
||||
embedData.PlainText = embedData.PlainText?.Replace("%user%", user.ToString()).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
|
||||
embedData.Description = embedData.Description?.Replace("%user%", user.ToString()).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
|
||||
embedData.Title = embedData.Title?.Replace("%user%", user.ToString()).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
|
||||
|
||||
rep.Replace(embedData);
|
||||
try
|
||||
{
|
||||
await channel.EmbedAsync(embedData.ToEmbed(), embedData.PlainText ?? "").ConfigureAwait(false);
|
||||
await channel.EmbedAsync(embedData.ToEmbed(), embedData.PlainText?.SanitizeMentions() ?? "").ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex) { _log.Warn(ex); }
|
||||
}
|
||||
else
|
||||
{
|
||||
var msg = conf.DmGreetMessageText.Replace("%user%", user.ToString()).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
|
||||
var msg = rep.Replace(conf.DmGreetMessageText);
|
||||
if (!string.IsNullOrWhiteSpace(msg))
|
||||
{
|
||||
await channel.SendConfirmAsync(msg).ConfigureAwait(false);
|
||||
@ -409,4 +413,4 @@ namespace NadekoBot.Services
|
||||
ChannelByeMessageText = g.ChannelByeMessageText,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
using Discord;
|
||||
using Discord.WebSocket;
|
||||
using NadekoBot.DataStructures.Replacements;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Services.Database;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
@ -20,13 +21,6 @@ namespace NadekoBot.Services.Utility
|
||||
|
||||
public string RemindMessageFormat { get; }
|
||||
|
||||
public readonly IDictionary<string, Func<Reminder, string>> _replacements = new Dictionary<string, Func<Reminder, string>>
|
||||
{
|
||||
{ "%message%" , (r) => r.Message },
|
||||
{ "%user%", (r) => $"<@!{r.UserId}>" },
|
||||
{ "%target%", (r) => r.IsPrivate ? "Direct Message" : $"<#{r.ChannelId}>"}
|
||||
};
|
||||
|
||||
private readonly Logger _log;
|
||||
private readonly CancellationTokenSource cancelSource;
|
||||
private readonly CancellationToken cancelAllToken;
|
||||
@ -82,11 +76,13 @@ namespace NadekoBot.Services.Utility
|
||||
if (ch == null)
|
||||
return;
|
||||
|
||||
await ch.SendMessageAsync(
|
||||
_replacements.Aggregate(RemindMessageFormat,
|
||||
(cur, replace) => cur.Replace(replace.Key, replace.Value(r)))
|
||||
.SanitizeMentions()
|
||||
).ConfigureAwait(false); //it works trust me
|
||||
var rep = new ReplacementBuilder()
|
||||
.WithOverride("%user%", () => $"<@!{r.UserId}>")
|
||||
.WithOverride("%message%", () => r.Message)
|
||||
.WithOverride("%target%", () => r.IsPrivate ? "Direct Message" : $"<#{r.ChannelId}>")
|
||||
.Build();
|
||||
|
||||
await ch.SendMessageAsync(rep.Replace(RemindMessageFormat).SanitizeMentions()).ConfigureAwait(false); //it works trust me
|
||||
}
|
||||
catch (Exception ex) { _log.Warn(ex); }
|
||||
finally
|
||||
|
Loading…
Reference in New Issue
Block a user