private async Task ExecuteProxy(Message trigger, MessageContext ctx, AutoproxySettings autoproxySettings, ProxyMatch match, bool allowEveryone, bool allowEmbeds) { // Create reply embed var embeds = new List <Embed>(); var content = ""; if (trigger.Type == Message.MessageType.Reply && trigger.MessageReference?.ChannelId == trigger.ChannelId) { var repliedTo = trigger.ReferencedMessage.Value; if (repliedTo != null) { if (trigger.Mentions.Length > 0 && repliedTo.Author.Id == trigger.Mentions[0].Id && !(trigger.Content.Contains($"<@{repliedTo.Author.Id}>") || trigger.Content.Contains($"<@!{repliedTo.Author.Id}>"))) { content = $"*<@{repliedTo.Author.Id}>*\n"; } var(nickname, avatar) = await FetchReferencedMessageAuthorInfo(trigger, repliedTo); var embed = CreateReplyEmbed(match, trigger, repliedTo, nickname, avatar); if (embed != null) { embeds.Add(embed); } } // TODO: have a clean error for when message can't be fetched instead of just being silent } // Send the webhook content += match.ProxyContent; if (!allowEmbeds) { content = content.BreakLinkEmbeds(); } var messageChannel = await _cache.GetChannel(trigger.ChannelId); var rootChannel = await _cache.GetRootChannel(trigger.ChannelId); var threadId = messageChannel.IsThread() ? messageChannel.Id : (ulong?)null; var guild = await _cache.GetGuild(trigger.GuildId.Value); var proxyMessage = await _webhookExecutor.ExecuteWebhook(new ProxyRequest { GuildId = trigger.GuildId !.Value, ChannelId = rootChannel.Id, ThreadId = threadId, Name = await FixSameName(messageChannel.Id, ctx, match.Member), AvatarUrl = AvatarUtils.TryRewriteCdnUrl(match.Member.ProxyAvatar(ctx)), Content = content, Attachments = trigger.Attachments, FileSizeLimit = guild.FileSizeLimit(), Embeds = embeds.ToArray(), Stickers = trigger.StickerItems, AllowEveryone = allowEveryone });
public static JObject ToJson(this AutoproxySettings settings, string?memberHid = null) { var o = new JObject(); // tbd o.Add("autoproxy_mode", settings.AutoproxyMode.ToString().ToLower()); o.Add("autoproxy_member", memberHid); return(o); }
private async Task <IActionResult> Get(AutoproxySettings settings) { string hid = null; if (settings.AutoproxyMember != null) { hid = (await _repo.GetMember(settings.AutoproxyMember.Value))?.Hid; } return(Ok(settings.ToJson(hid))); }
public static JObject ToJson(this AutoproxySettings settings, string?memberHid = null) { var o = new JObject(); // tbd o.Add("autoproxy_mode", settings.AutoproxyMode.ToString().ToLower()); o.Add("autoproxy_member", settings.AutoproxyMode == AutoproxyMode.Front ? null : memberHid); o.Add("last_latch_timestamp", settings.LastLatchTimestamp?.FormatExport()); return(o); }
private bool IsLatchExpired(MessageContext ctx, AutoproxySettings settings) { if (ctx.LatchTimeout == 0) { return(false); } var timeout = ctx.LatchTimeout.HasValue ? Duration.FromSeconds(ctx.LatchTimeout.Value) : DefaultLatchExpiryTime; return(_clock.GetCurrentInstant() - settings.LastLatchTimestamp > timeout); }
private async Task AutoproxyFront(Context ctx, AutoproxySettings settings) { if (settings.AutoproxyMode == AutoproxyMode.Front) { await ctx.Reply($"{Emojis.Note} Autoproxy is already set to front mode in this server. If you want to disable autoproxying, use `pk;autoproxy off`."); } else { await UpdateAutoproxy(ctx, AutoproxyMode.Front, null); await ctx.Reply($"{Emojis.Success} Autoproxy set to front mode in this server. Messages will now be autoproxied using the *current first fronter*, if any."); } }
private async Task AutoproxyLatch(Context ctx, AutoproxySettings settings) { if (settings.AutoproxyMode == AutoproxyMode.Latch) { await ctx.Reply($"{Emojis.Note} Autoproxy is already set to latch mode in this server. If you want to disable autoproxying, use `pk;autoproxy off`."); } else { await UpdateAutoproxy(ctx, AutoproxyMode.Latch, null); await ctx.Reply($"{Emojis.Success} Autoproxy set to latch mode in this server. Messages will now be autoproxied using the *last-proxied member* in this server."); } }
private async Task AutoproxyOff(Context ctx, AutoproxySettings settings) { if (settings.AutoproxyMode == AutoproxyMode.Off) { await ctx.Reply($"{Emojis.Note} Autoproxy is already off in this server."); } else { await UpdateAutoproxy(ctx, AutoproxyMode.Off, null); await ctx.Reply($"{Emojis.Success} Autoproxy turned off in this server."); } }
public bool TryMatch(MessageContext ctx, AutoproxySettings settings, IReadOnlyCollection <ProxyMember> members, out ProxyMatch match, string messageContent, bool hasAttachments, bool allowAutoproxy) { if (TryMatchTags(members, messageContent, hasAttachments, out match)) { return(true); } if (allowAutoproxy && TryMatchAutoproxy(ctx, settings, members, messageContent, out match)) { return(true); } return(false); }
private bool TryMatchAutoproxy(MessageContext ctx, AutoproxySettings settings, IReadOnlyCollection <ProxyMember> members, string messageContent, out ProxyMatch match) { match = default; // Skip autoproxy match if we hit the escape character if (messageContent.StartsWith(AutoproxyEscapeCharacter)) { throw new ProxyService.ProxyChecksFailedException( "This message matches none of your proxy tags, and it was not autoproxied because it starts with a backslash (`\\`)."); } // Find the member we should autoproxy (null if none) var member = settings.AutoproxyMode switch { AutoproxyMode.Member when settings.AutoproxyMember != null => members.FirstOrDefault(m => m.Id == settings.AutoproxyMember), AutoproxyMode.Front when ctx.LastSwitchMembers.Length > 0 => members.FirstOrDefault(m => m.Id == ctx.LastSwitchMembers[0]), AutoproxyMode.Latch when settings.AutoproxyMember != null => members.FirstOrDefault(m => m.Id == settings.AutoproxyMember.Value), _ => null }; // Throw an error if the member is null, message varies depending on autoproxy mode if (member == null) { if (settings.AutoproxyMode == AutoproxyMode.Front) { throw new ProxyService.ProxyChecksFailedException( "You are using autoproxy front, but no members are currently registered as fronting. Please use `pk;switch <member>` to log a new switch."); } if (settings.AutoproxyMode == AutoproxyMode.Member) { throw new ProxyService.ProxyChecksFailedException( "You are using member-specific autoproxy with an invalid member. Was this member deleted?"); } if (settings.AutoproxyMode == AutoproxyMode.Latch) { throw new ProxyService.ProxyChecksFailedException( "You are using autoproxy latch, but have not sent any messages yet in this server. Please send a message using proxy tags first."); } throw new ProxyService.ProxyChecksFailedException( "This message matches none of your proxy tags and autoproxy is not enabled."); } if (settings.AutoproxyMode != AutoproxyMode.Member && !member.AllowAutoproxy) { throw new ProxyService.ProxyChecksFailedException( "This member has autoproxy disabled. To enable it, use `pk;m <member> autoproxy on`."); } // Moved the IsLatchExpired() check to here, so that an expired latch and a latch without any previous messages throw different errors if (settings.AutoproxyMode == AutoproxyMode.Latch && IsLatchExpired(ctx, settings)) { throw new ProxyService.ProxyChecksFailedException( "Latch-mode autoproxy has timed out. Please send a new message using proxy tags."); } match = new ProxyMatch { Content = messageContent, Member = member, // We're autoproxying, so not using any proxy tags here // we just find the first pair of tags (if any), otherwise null ProxyTags = member.ProxyTags.FirstOrDefault() }; return(true); }
private async Task <IActionResult> Patch(PKSystem system, ulong?guildId, ulong?channelId, JObject data, AutoproxySettings oldData) { var updateMember = data.ContainsKey("autoproxy_member"); PKMember?member = null; if (updateMember) { member = await ResolveMember(data.Value <string>("autoproxy_member")); } var patch = AutoproxyPatch.FromJson(data, member?.Id); patch.AssertIsValid(); if (updateMember && member == null) { patch.Errors.Add(new("autoproxy_member", "Member not found.")); } if (updateMember && ((patch.AutoproxyMode.IsPresent && patch.AutoproxyMode.Value == AutoproxyMode.Latch) || oldData.AutoproxyMode == AutoproxyMode.Latch)) { patch.Errors.Add(new("autoproxy_member", "Cannot update autoproxy member if autoproxy mode is set to latch")); } if (patch.Errors.Count > 0) { throw new ModelParseError(patch.Errors); } var res = await _repo.UpdateAutoproxy(system.Id, guildId, channelId, patch); if (!updateMember && oldData.AutoproxyMember != null) { member = await _repo.GetMember(oldData.AutoproxyMember.Value); } return(Ok(res.ToJson(member?.Hid))); }
private async Task <Embed> CreateAutoproxyStatusEmbed(Context ctx, AutoproxySettings settings) { var commandList = "**pk;autoproxy latch** - Autoproxies as last-proxied member" + "\n**pk;autoproxy front** - Autoproxies as current (first) fronter" + "\n**pk;autoproxy <member>** - Autoproxies as a specific member"; var eb = new EmbedBuilder() .Title($"Current autoproxy status (for {ctx.Guild.Name.EscapeMarkdown()})"); var fronters = ctx.MessageContext.LastSwitchMembers; var relevantMember = settings.AutoproxyMode switch { AutoproxyMode.Front => fronters.Length > 0 ? await ctx.Repository.GetMember(fronters[0]) : null, AutoproxyMode.Member when settings.AutoproxyMember.HasValue => await ctx.Repository.GetMember(settings.AutoproxyMember.Value), _ => null }; switch (settings.AutoproxyMode) { case AutoproxyMode.Off: eb.Description($"Autoproxy is currently **off** in this server. To enable it, use one of the following commands:\n{commandList}"); break; case AutoproxyMode.Front: { if (fronters.Length == 0) { eb.Description("Autoproxy is currently set to **front mode** in this server, but there are currently no fronters registered. Use the `pk;switch` command to log a switch."); } else { if (relevantMember == null) { throw new ArgumentException("Attempted to print member autoproxy status, but the linked member ID wasn't found in the database. Should be handled appropriately."); } eb.Description($"Autoproxy is currently set to **front mode** in this server. The current (first) fronter is **{relevantMember.NameFor(ctx).EscapeMarkdown()}** (`{relevantMember.Hid}`). To disable, type `pk;autoproxy off`."); } break; } case AutoproxyMode.Member: { if (relevantMember == null) { // just pretend autoproxy is off if the member was deleted // ideally we would set it to off in the database though... eb.Description($"Autoproxy is currently **off** in this server. To enable it, use one of the following commands:\n{commandList}"); } else { eb.Description($"Autoproxy is active for member **{relevantMember.NameFor(ctx)}** (`{relevantMember.Hid}`) in this server. To disable, type `pk;autoproxy off`."); } break; } case AutoproxyMode.Latch: eb.Description("Autoproxy is currently set to **latch mode**, meaning the *last-proxied member* will be autoproxied. To disable, type `pk;autoproxy off`."); break; default: throw new ArgumentOutOfRangeException(); } if (!ctx.MessageContext.AllowAutoproxy) { eb.Field(new Embed.Field("\u200b", $"{Emojis.Note} Autoproxy is currently **disabled** for your account (<@{ctx.Author.Id}>). To enable it, use `pk;autoproxy account enable`.")); } return(eb.Build()); }