Coder Social home page Coder Social logo

notabot's People

Contributors

antoinejt avatar elanis avatar equinox4 avatar giregl avatar innocenzi avatar leroideskiwis avatar mesteery avatar rapougnac avatar sirlynix avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

notabot's Issues

In spam module, log the message that triggered the auto-mute

Hi,

We are using Not a Bot on our server with quite some success, the recent spam module is an handy addition. From time to time, there are false-positives and a legit member gets wrongly auto-muted; e.g. a member sending multiple pastebin-like links, one per file he is working on, to ask for assistance. The logged message "<@x> had been auto-mute because of spam in <#y>" is very nice because it gives us the opportunity to look at the logs and verify which message triggered the auto-mute, then our policy is either kick the spammer or unmute the unfortunate member.

Because we are moderating a quite busy server (10k+ members), our logs are quite dense and it is not always easy to determine which message triggered the auto-mute. My suggestion is to add the message that triggered the auto-mute (the one whose score exceeded the configured threshold, if it is easy to do) in the logs to ease our job. Maybe there could be an unmute button next to the message.

Regards,

[Proposition] Mise en place de l'api NotABot

Cette issue n'est pas une RFC, néanmoins je vais essayer de décrire de la manière la plus succincte ce que je vois pour l'api.

Pour la structure de l'api, je pense à quelque chose du genre avec l'ensemble des actions possibles :

  • https://notabot.fr/api/v1/guild/323076998576603137 : Permet de récupérer les informations d'une guild (serveur)

J'imagine aussi la possibilité de filtrer certains champs, comme avoir un paramètre GET afin de récupérer une seule information par exemple :

https://notabot.fr/api/v1/guild/323076998576603137?fields=memberCount,description

Certaines informations ne sont accessibles qu'a des personnes ayant des permissions particulières. Je propose donc cette approche pour l'authentification :

  1. Connexion OAuth 2 avec Discord (afin de récupérer un token d'autorisation et l'identifiant de l'utilisateur)
  2. L'utilisateur effectue une requête pour modifier un rôle sur le serveur par exemple, il passe en paramètre le token d'autorisation
  3. L'api va vérifier au prêt du serveur si l'utilisateur est bien sur le serveur, et s'il possède les droits nécessaires pour effectuer l'action
  4. Si c'est le cas, le serveur modifie bien le rôle et renvoie une réponse 200. Le cas échéant, il renvoie une 401 / 403

Il serait aussi bien de pouvoir de désactiver l'accès au serveur depuis l'api, pour x / y raisons. Ou au moins avoir un contrôle avancé concernant les permissions.

Concernant les informations globales du bot, je pense aux possibilités suivantes :

  • https://notabot.fr/api/v1/guilds : Retourne les serveurs sur lequel est le bot
  • https://notabot.fr/api/v1/guild/323076998576603137/stats : Retourne les statistiques d'un serveur
  • https://notabot.fr/api/v1/guild/323076998576603137/channels?type=TextChannel : Récupère les canaux textes d'un serveur
  • https://notabot.fr/api/v1/about : Retourne les informations du bot, comme le fait s'il est en ligne, sa présence, ect...

Je mets en évidence le fait que c'est un exemple, l'idée est (au début) surtout d'avoir une API qui permet de récupérer des informations basiques.

Moi, je voudrais par exemple communiquer avec l'api pour faire des statistiques.
C'est impossible de proposer une API complète dés le début, donc il faut y aller step par step.

[modo] [ban] Add a softban feature

📝 Proposal:

Add a softban feature that consists to ban a member and delete all his messages, and unban it directly after.
This could be useful in case of compromised accounts spamming advertisement where you want all the messages of the user deleted (in the last 24h for example), and don't want the user to stay banned (for him to be able to join back when he gets his account back).

Spam true-negative and false-positive

KEEP ME OPEN

This issue is for tracking when the spam-submodule wrongly muted a member (false-positive) and when it didn't muted a spam (false-negative). Each entry in this issue serves as database for future updates of the spam-submodule, such updates should reference the entries in this issue but should not close the issue.

[spec] One-word commands

What

The goal is to have a way to bind single-word commands to text blocks.

As an example, the !ask s-w command would make the bot print the following
text block.

"To maximise your chances at getting an answer, you should..."

Why

On help-oriented servers and channels, there are a lot of cases where the
"helpers" may need to regularly repeat the same messages, e.g. "give some info".

On a more "general" case, this can be used as a rule reminder, info. block, etc.

How

P1: A simple key-based message posting

The basics

Existing NaB modules are triggered based on a word.

In this case, the module may have a lot of words, and the same behaviour on
each trigger.

To avoid loading a lot of redundant instances of the same module, we should
develop a module that'd be triggered on every message, and post the right
message if needed.

Message storing and loading

A message is associated to its trigger word, its "key".

The most primitive implementation of such a feature would be to have a folder,
each file being named after the key.

The simplest way to name the file would be to only put the key.
Making extensions optional could simplify integration, but slow
down the module for "not in cache" keys.

On trigger, the bot would then load the message found inside the file, before
posting it on Discord.

Simple optimizations to do to avoid high disk IO/latency would be:

  • On startup, make the bot scan the folder supposedly containing messages, and
    keep the list of found files in a "key" cache.
  • On file read (command trigger with an existing associated message), keep the
    message content in cache along the key.

The base naïve algorithm then becomes:

On incoming message
// Taking the first item, removing the trigger character
key := split(message, " ", 2)[0][1:]
if key in loaded_messages then
	send(channel, loaded_messages[key])
// startup analysis, and FS check
else if key in found_keys or exists(key)
	loaded_messages[key] = load(key)
	send(channel, loaded_messages[key])
else pass

P2: Let the users remove bot-posted messages

Allowing the users to remove the messages the bot posted after being triggered
may help with self-moderation, spam avoidance (letting the users react to
a trigger spammer), or just to let them clear the chat once the message
have been read.

This could be done by allowing the user react on the message by adding a given
emoji reaction, which would remove the bot message.

Still, a naïve implementation of this feature may make the bot remove other
messages it posted, that aren't in the scope of this module.

For that, either the message should contain a flag to recognize it without
any bot-side cache (e.g. starting the message with a known keyword),
or the bot should keep a list of every answer it posted (message ID).

To avoid growing memory consumption, expiration would be required on the time
users have to remove the message.

The cache could be either in the NaB code (with a garbage-collector running
to reap every expired message watch) or in some specialized cache servers, like
Redis, which natively supports expiration on keys.

[message] Add !addalias and !editreply command

Add !addalias command to add an alias to a !addreply answer to prevent duplicating commands.
And !editreply to change behavior of a reply for a command and its aliases

Example:

!addreply !test My answer
!addalias !foo !test

!foo

My answer

!editreply !test My **amazing** answer
!foo

My amazing answer

Create Documentation

I think a documentation can be good for this bot because, i'm noob you know and it's so difficult for me to understand how to setup the bot. I know you have a discord server for this project but i think a doc can be better x)

Offer a config for the preffix or use @notabot as a prefix

It is annoying that multiple bots are using the same ! as prefix. There are places where NaB was added just for a very few modules and the admins wanted to replace ! by something else for NaB.

I'm suggesting to make it configurable and to add another hard-coded prefix. If the first word is a mention to the bot, it could be considered as a prefix i.e. you could do @notabot enable raid

image

image

ajouter des exemples pour les commandes

Le bot étant composé d'énormément de commandes, je pense qu'il serait préférable d'ajouter un exemple de comment utiliser une commande.
Lorsque la commande !config est faite, on obtient la liste de configurations et je pense qu'un exemple en dessous de chaque élément configurable serait nécessaire pour éviter de devoir sans arrêt demander de l'aide sur le discord.
Je dis pas que c'est une mauvaise chose de demander de l'aide, mais pour des insomniaques comme moi, parfois on peut demander de l'aide très tardivement et ne pas avoir de réponse...
Sinon bot toujours aussi clean, je recommande

Improve `logs` module to log username changes and channel creation/deletion

In the process to stop using Dyno on "Not a name" server, we will need these features to be added in Not A Bot.

The current logs module is used to log deleted messages from channels. We also need to log username changes and channel (and if possible Threads) creation/deletion to make moderation task easier.

Changement de librairie pour NaB

Hello, je viens faire une proposition de modification (majeure) pour NaB :
Mon idée serait de réécrire NaB, avec une librairie plus ''connue'' ou plutôt ayant une plus grosse communauté. Vous me direz sûrement que c'est une idée inutile (et c'est sûrement le cas), j'ai donc pesé le pour et le contre des deux librairies qui sont le plus adaptées pour un tel travail de réécriture selon moi, ainsi que pour la possibilité de rester avec discordia.

discord.py discord.js discordia
Avantages Librairie fréquemment mise à jour (actuellement en train d'être réécrite) ; Grande communauté (donc plus de possibilités de contribution) ; Langage facile à prendre en main ; Plus de librairies tierces pour ajouter du fun Librairie Discord la plus utilisé (grande communauté) ; Possibilité de faire une API NaB plus facilement #25 ; Possibilité d'intégration d'un dashboard Aucun travail de réécriture
Inconvénients Travail de réécriture à faire ; Performances du langage (négligeable vu que c'est des appels à l'API Discord) Travail de réécriture à faire La communauté de "développeurs discordia" est plus petite que celles des autres librairies ; Pour beaucoup de personnes, il leur faudra apprendre Lua si elles veulent contribuer

Voilà ce que j'en ai conclu, j'attends vos avis pour savoir si c'est une énième folie de ma part ou bien si je suis un génie du 21ème siècle.

Add `userinfo` command to get user information

In the process to stop using Dyno on "Not a name" server, we will need this command to be added in Not A Bot.

userinfo log as many as possible information about the current user. Example:
image

If some more information are available and useful in a moderating context, they can be added to the embed as well.

In userinfo, include an activity graph

When I use !userinfo, I'm interested in knowing when the user account was created and when did he join the server. When it is an user that has joined the server several months/years ago, I'm interested in knowing when the user was most active and in which channel.

What would be great is to see an activity graph, an image that show its activity grouped by month.

Example, in the image below, we see that the member was most active around September 2021

image

Then the bot should also logs the 3 channels the user posted the most and how many messages each time.

Include ReadTheBin features inside of NotABot

The bot read-the-bin is a bot that scan messages to find long snippet of codes, i.e. 12+ lines of markdown code-blocks. When such a message is found, it automatically uploads the content of the message to https://bin.readthedocs.fr, my own pastebin-like website. The objective of this bot is to prevent users from posting code that would take the entire screen.

The read-the-bin bot is not always available and its development staled. We suggest to re-implement it our way inside of not-a-bot.

[modo] Permissions should be checked on interaction

Expected Behaviour:

The permissions of the moderator are checked before each action to ensure he is allowed to do the specified action (like ban, mute, open tickets, etc.)

⚠️ Actual Behaviour:

Any member with an access to the channel that clicks on an interaction can ban, mute, etc. without any permission check

📝 Proposal:

Add a permission check to see if the member/moderator clicking is allowed to execute the specific action.


NotaBot/module_modo.lua

Lines 513 to 793 in b1c8f37

function Module:OnInteractionCreate(interaction)
local guild = interaction.guild
if not guild then
return
end
local data = self:GetPersistentData(guild)
local alertMessage = data.AlertMessages[interaction.message.id]
if not alertMessage then
-- Not our job
return
end
local moderator = interaction.member
local actionStr
local interactionType = interaction.data.custom_id
if interactionType == "alertmodule_dismiss" then
if alertMessage.Dismissed then
interaction:respond({
type = enums.interactionResponseType.channelMessageWithSource,
data = {
content = "❎ Alert has already been dismissed",
flags = enums.interactionResponseFlag.ephemeral
}
})
return
end
alertMessage.Dismissed = true
-- Disable dismiss button
alertMessage.Components[2].components[1].disabled = true
interaction:respond({
type = enums.interactionResponseType.channelMessageWithSource,
data = {
content = "✅ Alert has been dismissed (it won't trigger mute nor ping)",
flags = enums.interactionResponseFlag.ephemeral
}
})
actionStr = "Dismissed by " .. moderator.mentionString
elseif interactionType == "alertmodule_deletemessage" then
-- "Waiting"
interaction:respond({
type = enums.interactionResponseType.deferredChannelMessageWithSource,
data = {
flags = enums.interactionResponseFlag.ephemeral
}
})
local channel, err = client:getChannel(alertMessage.ChannelId)
if not channel then
interaction:editResponse({
content = string.format("❌ failed to retrieve message channel: %s", err)
})
return
end
local message, err = channel:getMessage(alertMessage.MessageId)
if not message then
interaction:editResponse({
content = string.format("❌ failed to retrieve message: %s", err)
})
return
end
local success, err = message:delete()
if not success then
interaction:editResponse({
content = string.format("❌ failed to delete message: %s", err)
})
return
end
interaction:editResponse({
content = "✅ the message was deleted"
})
actionStr = "Deleted by " .. moderator.mentionString
elseif interactionType == "alertmodule_modmail" then
local modmail = Bot:GetModuleForGuild(guild, "modmail")
if not modmail then
interaction:respond({
type = enums.interactionResponseType.channelMessageWithSource,
data = {
content = "❌ The modmail module isn't enabled on this server",
flags = enums.interactionResponseFlag.ephemeral
}
})
return
end
-- "Waiting"
interaction:respond({
type = enums.interactionResponseType.deferredChannelMessageWithSource,
data = {
flags = enums.interactionResponseFlag.ephemeral
}
})
local targetMember, err = guild:getMember(alertMessage.ReportedUserId)
if not targetMember then
interaction:editResponse({
content = string.format("❌ failed to retrieve member: %s", err)
})
return
end
local reason = string.format("%s has opened a ticket following your message (<https://discord.com/channels/%s/%s/%s>)", moderator.mentionString, guild.id, alertMessage.ChannelId, alertMessage.MessageId)
local ticketChannel, err = modmail:OpenTicket(moderator, targetMember, reason, true)
if not ticketChannel then
interaction:editResponse({
content = string.format("❌ failed to open modmail ticket: %s", err)
})
return
end
interaction:editResponse({
content = "✅ a modmail ticket has been created: " .. ticketChannel.mentionString
})
actionStr = "Modmail ticket opened by " .. moderator.mentionString
elseif interactionType == "alertmodule_mute" then
local mute = Bot:GetModuleForGuild(guild, "mute")
if not mute then
interaction:respond({
type = enums.interactionResponseType.channelMessageWithSource,
data = {
content = "❌ The mute module isn't enabled on this server",
flags = enums.interactionResponseFlag.ephemeral
}
})
return
end
local duration = interaction.data.values and tonumber(interaction.data.values[1]) or nil
if not duration then
interaction:respond({
type = enums.interactionResponseType.channelMessageWithSource,
data = {
content = "❌ an error occurred (invalid duration)",
flags = enums.interactionResponseFlag.ephemeral
}
})
return
end
-- "Waiting"
interaction:respond({
type = enums.interactionResponseType.deferredChannelMessageWithSource,
data = {
flags = enums.interactionResponseFlag.ephemeral
}
})
local success, err = mute:Mute(guild, alertMessage.ReportedUserId, duration)
if not success then
interaction:editResponse({
content = string.format("❌ failed to open mute member: %s", err)
})
return
end
interaction:editResponse({
content = "✅ the member has been muted for " .. util.FormatTime(duration)
})
actionStr = "Muted " .. util.DiscordRelativeTime(duration) .. " by " .. moderator.mentionString
elseif interactionType == "alertmodule_ban" then
local duration = interaction.data.values and interaction.data.values[1] or nil
if duration == "0" or duration == "0_deletemessages" then
-- "Waiting"
interaction:respond({
type = enums.interactionResponseType.deferredChannelMessageWithSource,
data = {
flags = enums.interactionResponseFlag.ephemeral
}
})
local purgeDays = duration == "0_deletemessages" and 1 or 0
local reason = "banned by " .. moderator.name .. " via alert"
local success, err = guild:banUser(alertMessage.ReportedUserId, "banned by " .. moderator.name .. " via alert", purgeDays)
if not success then
interaction:editResponse({
content = string.format("❌ failed to open ban user: %s", err)
})
return
end
local ban = Bot:GetModuleForGuild(guild, "ban")
if ban then
ban:RegisterBan(guild, alertMessage.ReportedUserId, moderator, 0, reason)
end
interaction:editResponse({
content = "✅ the member has been banned" .. (purgeDays > 0 and " (and its last 24h message deleted)" or "")
})
actionStr = "banned permanently by " .. moderator.mentionString .. (purgeDays > 0 and " (last 24h message deleted)" or "")
else
duration = duration and tonumber(duration) or nil
if not duration then
interaction:respond({
type = enums.interactionResponseType.channelMessageWithSource,
data = {
content = "❌ an error occurred (invalid duration)",
flags = enums.interactionResponseFlag.ephemeral
}
})
return
end
-- Temp ban
local ban = Bot:GetModuleForGuild(guild, "ban")
if not ban then
interaction:respond({
type = enums.interactionResponseType.channelMessageWithSource,
data = {
content = "❌ The ban module isn't enabled on this server (and is required for temporary ban)",
flags = enums.interactionResponseFlag.ephemeral
}
})
return
end
-- "Waiting"
interaction:respond({
type = enums.interactionResponseType.deferredChannelMessageWithSource,
data = {
flags = enums.interactionResponseFlag.ephemeral
}
})
local durationStr = util.FormatTime(duration)
local success, err = guild:banUser(alertMessage.ReportedUserId, "banned by " .. moderator.name .. " via alert for " .. durationStr)
if not success then
interaction:editResponse({
content = string.format("❌ failed to open ban user: %s", err)
})
return
end
ban:RegisterBan(guild, alertMessage.ReportedUserId, moderator, duration, reason)
interaction:editResponse({
content = "✅ the member has been banned for " .. durationStr
})
actionStr = "banned for " .. durationStr .. " by " .. moderator.mentionString
end
else
interaction:respond({
type = enums.interactionResponseType.channelMessageWithSource,
data = {
content = "❌ an error occurred (unknown interaction type " .. tostring(interactionType) .. ")"
}
})
return
end
if actionStr then
local currentActionStr = alertMessage.Embed.fields[5].value
if currentActionStr == "None" then
alertMessage.Embed.fields[5].value = actionStr
else
alertMessage.Embed.fields[5].value = currentActionStr .. "\n" .. actionStr
end
interaction.message:update({
components = alertMessage.Components,
embed = alertMessage.Embed
})
end
end

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.