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:
Master Kwoth 2017-06-28 02:44:30 +02:00
parent aa5b7f96c7
commit 942f15cf05
14 changed files with 328 additions and 210 deletions

View File

@ -36,14 +36,4 @@ For example:
Now if you try to trigger `/o/`, it won't print anything.
###Placeholders!
There are currently three different placeholders which we will look at, with more placeholders potentially coming in the future.
| Placeholder | Description | Example Usage | Usage |
|:-----------:|-------------|---------------|-------|
|`%mention%`|The `%mention%` placeholder is triggered when you type `@BotName` - It's important to note that if you've given the bot a custom nickname, this trigger won't work!|```.acr "Hello %mention%" I, %mention%, also say hello!```|Input: "Hello @BotName" Output: "I, @BotName, also say hello!"|
|`%user%`|The `%user%` placeholder mentions the person who said the command|`.acr "Who am I?" You are %user%!`|Input: "Who am I?" Output: "You are @Username!"|
|`%rng%`|The `%rng%` placeholder generates a random number between 0 and 10. You can also specify a custom range (%rng1-100%) even with negative numbers: `%rng-9--1%` (from -9 to -1) . |`.acr "Random number" %rng%`|Input: "Random number" Output: "2"|
|`%rnduser%`|The `%rnduser%` placeholder mentions a random user from the server. |`.acr "Random user" %rnduser%`|Input: "Random number" Output: @SomeUser|
|`%target%`|The `%target%` placeholder is used to make Nadeko Mention another person or phrase, it is only supported as part of the response|`.acr "Say this: " %target%`|Input: "Say this: I, @BotName, am a parrot!". Output: "I, @BotName, am a parrot!".|
Thanks to Nekai for being creative. <3
To learn about placeholders, go [here](Placeholders.md)

24
docs/Placeholders.md Normal file
View 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)

View File

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

View File

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

View File

@ -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());
}
}
}

View 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);
}
}
}

View File

@ -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);

View File

@ -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 ?? "")
new ReplacementBuilder()
.WithDefault(Context)
.Build()
.Replace(crembed);
await Context.Channel.EmbedAsync(crembed.ToEmbed(), crembed.PlainText?.SanitizeMentions() ?? "")
.ConfigureAwait(false);
}
catch (Exception ex)
{
_log.Warn("Sending CREmbed failed");
_log.Warn(ex);
}
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 ?? "")
new ReplacementBuilder()
.WithDefault(Context)
.Build()
.Replace(crembed);
await Context.Channel.EmbedAsync(crembed.ToEmbed(), crembed.PlainText?.SanitizeMentions() ?? "")
.ConfigureAwait(false);
}
catch (Exception ex)
else
{
_log.Warn("Sending CREmbed failed");
_log.Warn(ex);
}
return;
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()); }
}
}

View File

@ -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);

View File

@ -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">

View File

@ -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();
RotatingStatusMessages = _bc.RotatingStatusMessages;
RotatingStatuses = _bc.RotatingStatuses;
_rep = new ReplacementBuilder()
.WithClient(client)
.WithStats(client)
.WithMusic(music)
.Build();
_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()},
};
}
}

View File

@ -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());
}
}
}

View File

@ -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);

View File

@ -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