Coder Social home page Coder Social logo

southclaws / cj Goto Github PK

View Code? Open in Web Editor NEW
41.0 11.0 31.0 6.86 MB

CJ is a Discord bot that hangs around in the open.mp/burgershot.gg community discord.

License: GNU General Public License v3.0

Go 98.87% Makefile 0.80% Dockerfile 0.33%
discord discord-bot discordgo golang-application

cj's Introduction

SA:MP/open.mp Discord Bot

All Contributors

CJ

Development

This project is open to anyone who wants to contribute, large or small! Whether you noticed a typo or want to add a whole new feature, go for it!

Large additions should be discussed in issues or on Discord first. If you're new to Golang, ask me on Discord for where to start and you can use CJ as a starting point for a contribution.

Testing/Workflow

To run the app, you need:

  • A Discord server to test - you can't use the SA:MP Discord to do tests
  • Go 1.11 - no guarantees on older versions
  • A Discord bot with the 'Server Members Intent' enabled

If you don't own/admin a Discord server, creating one is simple, you can do it from the same menu you join discord servers from.

Running with a database

If you want to develop features that require persisting data, spin up a MongoDB database. If you have Docker installed, this is as simple as running make mongodb which will start a MongoDB container with a user root that has no password. If you don't have Docker, you'll need to install MongoDB onto your system..

Running without a database

If you don't need a database for your feature, just add NO_DATABASE=true to .env.

Finally, the application requires some configuration. Copy the example.env to .env and modify it to use your token and various IDs. Depending on what you're working on, some values won't be necessary.

Now you can build and run the application with make local.

Contributors โœจ

Thanks goes to these wonderful people (emoji key):

Octavian Dima
Octavian Dima

๐Ÿ’ป ๐Ÿค” ๐Ÿ›
J0shES
J0shES

๐Ÿ’ป ๐Ÿค” ๐Ÿ› ๐Ÿšง
Dayvison
Dayvison

๐Ÿ’ป
Adrian Graber
Adrian Graber

๐Ÿ’ป
__SyS__
__SyS__

๐Ÿ’ป
Robster
Robster

๐Ÿ–‹
Lasha Kanteladze
Lasha Kanteladze

๐Ÿ’ป โš ๏ธ ๐Ÿค”
Kristo Isberg
Kristo Isberg

๐Ÿ’ป โš ๏ธ ๐Ÿค”
Marcel Schramm
Marcel Schramm

๐Ÿ’ป
Codeah
Codeah

๐Ÿ’ป
Giampaolo Falqui
Giampaolo Falqui

๐Ÿ“–
Sunehildeep
Sunehildeep

๐Ÿ’ป
TommyB
TommyB

๐Ÿ–‹
Nikola Yanakiev
Nikola Yanakiev

๐Ÿ–‹

This project follows the all-contributors specification. Contributions of any kind welcome!

cj's People

Contributors

adrfranklin avatar agraber avatar alasnkz avatar alilogic avatar amyrahmady avatar bigeti avatar bios-marcel avatar brarharjot avatar bronzal avatar coool9 avatar dayvison avatar dependabot-preview[bot] avatar dependabot[bot] avatar giampy5 avatar gigabitzz avatar hual avatar imover00 avatar jtylr avatar notunlikethewaves avatar southclaws avatar sreyas-sreelal avatar sunehildeep avatar thecodeah avatar tommyb123 avatar wopss 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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cj's Issues

CJ to delete his response if the request message was deleted

Now users can spam '/whois' and similar mentioning commands for user info, then they can delete their messages, so CJ will look bugged/creepy if you forget that users can delete their posts. Solutions to this (I don't know if they are possible):

  1. "Store" the requesting-message ID somehow in the message CJ sent and if that message ID was deleted, let CJ delete his message too.
  2. Just add a mention in CJ's response message to the guy that requested that info.
  3. I don't think that it is possible to block users to delete/modify their messages, but it would be great to prohibit editing/deleting messages that requested a response from CJ. This would also solve the issue where someone requests other guy's info and then edits his request message and mentions somebody else, it can be confusing.

1 or 3 would be awesome if possible, 2 is pretty noobish and would look pretty bad.

P.S.: CJ you busta

Localisation

In the future, I would like to add the ability to translate the bot via simple text files if the need arises.

Just to keep things super simple:

  • Directory for each language
  • File for each message
  • Validation and loading into memory on main

Since most messages don't include formatting or only a single format specifier, there won't be any issues with article ordering (like the "Spanish dog black" problem and associated issues with localisation!)

Story mode with /impersonate

[1:51 PM] Codeah: @Southclaws Add a command to make CJ construct stories using /impersonate with different users
[1:51 PM] Southclaws: ok
[1:52 PM] Codeah: Woah
[1:52 PM] Southclaws: move the existing /impersonate code into a function, then just make a /story command that calls it in a loop 10 times
[1:52 PM] Southclaws: with Username: message format
[1:52 PM] Southclaws: EZ PZ

Possibility for infinite loop with /impersonate

If the output of /impersonate produces another /impersonate command, then it looks like that command will execute too (operators exempt from cooldown?). This may get progressively worse as more and more people use the command.

Command typo suggestions

This is an easy one:

  • user types a command
  • command does not match any triggers
  • for each trigger, compute the word distance (using Levenshtein or better)
  • suggest the command with the lowest distance score

Lowercase incoming commands

Verify was seen as an unknown command, it should be normalised to verify internally.

This should be easy to implement inside bot_private.go by just converting the first word of message.Content to lowercase.

Move command configuration to database instead of static environment variables

The whole "primary" and "admin" channel thing is really restrictive. I also don't want to hard-code channel IDs into the code.

A solution to this would be to create a collection with a single object that would be configuration for commands.

This would allow runtime modification of:

  • which channels a command is valid in
  • cooldown lengths
  • enable/disable commands

And probably more, maybe even per-command stuff.

Problem with /userinfo command

When a user has more than 1000 posts it will fail to convert it to an integer because the comma.

@Southclaws's profile:

  • Join Date: Apr 2009
  • Total Posts: 4,637
  • Reputation: 1204

What CJ return when someone execute /userinfo:

Username: [HLF]Southclaw Member since: 22/04/2009 Total posts: 0 Reputation: 1204

Add "helper" feature for scripting channel

Allow experienced users to register as "helpers".
If these users are in the "online" state (not "busy" or otherwise) and someone asks a question in the "scripting" channel, CJ should post a list of available "helper" users who may be able to help.

Otherwise, if everyone who is a registered helper is offline, CJ could let the user know that helpers are offline and their question may not be answered and, if no one does answer it, they should try again another time.

Question detection should be a simple matter of checking for question words, question marks or common words found in questions such as "anyone" or "can someone".

Cooldown on this feature is important, a good 10 minutes or so between.

Handle kicks properly

When a user is kicked, they lose the verified role and when they rejoin they can't join channels and they can't use the verify command because they are still verified in the database.

Solution would be to either

  • re-apply verified role to new users if they exist in the database
  • remove verified role when a user leaves the server (either through kick or voluntarily)

CJ cannot connect to forum to verify accounts due to Cloudflare

Possible solution: https://github.com/cardigann/go-cloudflare-scraper

To any forum mods reading this: CJ is very low-impact, probably produces far less traffic than an average user so I believe circumventing Cloudflare is okay - if not, drop a message here. On Discord, we use CJ to validate that a given Discord user is who they say they are to avoid the drama of impersonation. It also provides a few neat features such as user lookups, reputation and post counts.

Only check `#inlinemodform` children on new post alert

profile, err := fc.GetUserProfilePage("http://forum.sa-mp.com/member.php?u=" + id)

Instead of checking the entire page for changes, it should only check the element that contains posts (XPath //*[@id="inlinemodform"]) so it doesn't capture updates from other sources resulting in false positives.

And, if the page is being parsed, may as well add a link to the new post itself rather than just the post list.

For this, you'll need to parse the HTML in a similar way that the other forum features work. This is a useful function for starting off and you can read some of the code around here to get an idea of how it uses an XPath to identify a specific element. After that, it's simply a case of producing a hash of the contents, that's up to you!

Roles not working

Call to GuildMemberRoleAdd results in: "unknown role", cause unknown.

linuxthefish isn't included in the statistics

Hello,

When the statistics for the SA-MP server appear, the username of linuxthefish isn't included. This user has posted many messages, and so should be included in the statistics.

Please fix this ASAP, thanks.
image

Possible /whois output fix when not correctly mentioning

I am not very sure about this but maybe u can try.

[19:00] coool: /whois @nigglypuff
[19:00] BOTCJ: You need to mention someone to use this command.%!(EXTRA string=%s)

What about u change line 75

to

ErrorMessage: app.locale.GetLangString("en", "CommandErrorNoMention"),

just like the others? As /userinfo displays it without

[11:58] michaelbelgium: /userinfo bla
[11:58] BOTCJ: You need to mention someone to use this command.

Move to another database

I want to move away from SQLite just so I have a bit more flexibility with the data.

I'm thinking either Postgres or MongoDB...

Markov chain sentence generator

A fun and simple chat-bot feature often seen in IRC and Twitter bots:

Oh each chat message, run a stop-word removal and N-gram extractor over the contents of the text. Cache the result and flush the cache to an N-gram table every now and then.

Sentences can be generated from the N-gram table, either with a simple weighted selection with N words or with a more complex stop-word insertion method to build more legible sentences - probably out of the scope of this but the stop-word insertion could be trained on sentence samples very easily.

I actually plan to use Spacy for this task and delegate the actual sentence generation to an external app that will "top up" the database with new sentences every now and then (again, it doesn't need to be frequent and can depend on the usage rate). With Spacy, I'll run POS tagging over the N-grams to help build more legible sentences with basic logical structures.

Count user's message reactions

We all know whoever posts the spiciest meme gets the most reactions. Let's count them.

Since there isn't a gateway event for reaction creation (as far as I can see) this one will need to be a polling function that grabs the last X messages and counts reactions for them and distributes the score to the relevant users.

Command for checking a user's forum stats

Requires some xpath/web scraping knowledge:

  • Forum post count
  • Forum reputation
  • Total visitor messages
  • Anything else interesting about a user on the forum that can be retrieved using the user's ID (mine is 50199 for example, you can access my profile page, all posts, all threads, posts-per-day, etc.)

Send messages as CJ

From the administration channel, a command to speak on behalf of CJ would be fun.

Dependabot can't resolve your Go dependency files

Dependabot can't resolve your Go dependency files.

As a result, Dependabot couldn't update your dependencies.

The error Dependabot encountered was:

go: golang.org/x/[email protected]: unknown revision 2491c5de3490

If you think the above is an error on Dependabot's side please don't hesitate to get in touch - we'll do whatever we can to fix it.

You can mention @dependabot in the comments below to contact the Dependabot team.

Error on /userinfo command

/userinfo DobbysGamertag
<@%!s(MISSING)>, you need to mention someone to use this command.

Seems like if there's no @ tag it errors out.

HTTP Requests Failing

For some reason, requests to the forum are timing out:

{
  "level": "error",
  "ts": "2018-12-05T07:33:18.640Z",
  "caller": "bot/discord.go:119",
  "msg": "unhandled error from OnMessage",
  "error": "failed to get HTML root for user page: failed to perform request for https://forum.sa-mp.com/member.php?u=140150: Get https://forum.sa-mp.com/member.php?u=140150: failed to create new roundtrip from given transport: dial tcp 54.39.167.47:443: i/o timeout",
  "errorVerbose": "Get https://forum.sa-mp.com/member.php?u=140150: failed to create new roundtrip from given transport: dial tcp 54.39.167.47:443: i/o timeout\nfailed to perform request for https://forum.sa-mp.com/member.php?u=140150\ngithub.com/Southclaws/cj/forum.(*ForumClient).GetHTMLRoot\n\t/cj/forum/html.go:19\ngithub.com/Southclaws/cj/forum.(*ForumClient).GetUserProfilePage\n\t/cj/forum/forum.go:53\ngithub.com/Southclaws/cj/bot/commands.(*CommandManager).UserConfirmsProfile\n\t/cj/bot/commands/cmd_verify.go:212\ngithub.com/Southclaws/cj/bot/commands.(*CommandManager).commandVerify\n\t/cj/bot/commands/cmd_verify.go:61\ngithub.com/Southclaws/cj/bot/commands.(*CommandManager).commandVerify-fm\n\t/cj/bot/commands/all_commands.go:22\ngithub.com/Southclaws/cj/bot/commands.(*CommandManager).OnMessage\n\t/cj/bot/commands/command_manager.go:135\ngithub.com/Southclaws/cj/bot.(*App).onMessage\n\t/cj/bot/discord.go:117\ngithub.com/Southclaws/cj/bot.(*App).onMessage-fm\n\t/cj/bot/discord.go:33\ngithub.com/bwmarrin/discordgo.messageCreateEventHandler.Handle\n\t/go/pkg/mod/github.com/bwmarrin/[email protected]/eventhandlers.go:497\nruntime.goexit\n\t/usr/local/go/src/runtime/asm_amd64.s:1333\nfailed to get HTML root for user page\ngithub.com/Southclaws/cj/forum.(*ForumClient).GetUserProfilePage\n\t/cj/forum/forum.go:55\ngithub.com/Southclaws/cj/bot/commands.(*CommandManager).UserConfirmsProfile\n\t/cj/bot/commands/cmd_verify.go:212\ngithub.com/Southclaws/cj/bot/commands.(*CommandManager).commandVerify\n\t/cj/bot/commands/cmd_verify.go:61\ngithub.com/Southclaws/cj/bot/commands.(*CommandManager).commandVerify-fm\n\t/cj/bot/commands/all_commands.go:22\ngithub.com/Southclaws/cj/bot/commands.(*CommandManager).OnMessage\n\t/cj/bot/commands/command_manager.go:135\ngithub.com/Southclaws/cj/bot.(*App).onMessage\n\t/cj/bot/discord.go:117\ngithub.com/Southclaws/cj/bot.(*App).onMessage-fm\n\t/cj/bot/discord.go:33\ngithub.com/bwmarrin/discordgo.messageCreateEventHandler.Handle\n\t/go/pkg/mod/github.com/bwmarrin/[email protected]/eventhandlers.go:497\nruntime.goexit\n\t/usr/local/go/src/runtime/asm_amd64.s:1333",
  "stacktrace": "github.com/Southclaws/cj/bot.(*App).onMessage\n\t/cj/bot/discord.go:119\ngithub.com/Southclaws/cj/bot.(*App).onMessage-fm\n\t/cj/bot/discord.go:33\ngithub.com/bwmarrin/discordgo.messageCreateEventHandler.Handle\n\t/go/pkg/mod/github.com/bwmarrin/[email protected]/eventhandlers.go:497"
}
failed to get HTML root for user page:
failed to perform request for https://forum.sa-mp.com/member.php?u=140150:
Get https://forum.sa-mp.com/member.php?u=140150:
failed to create new roundtrip from given transport:
dial tcp 54.39.167.47:443: i/o timeout

Get https://forum.sa-mp.com/member.php?u=140150: failed to create new roundtrip from given transport: dial tcp 54.39.167.47:443: i/o timeout
failed to perform request for https://forum.sa-mp.com/member.php?u=140150
github.com/Southclaws/cj/forum.(*ForumClient).GetHTMLRoot
	/cj/forum/html.go:19
github.com/Southclaws/cj/forum.(*ForumClient).GetUserProfilePage
	/cj/forum/forum.go:53
github.com/Southclaws/cj/bot/commands.(*CommandManager).UserConfirmsProfile
	/cj/bot/commands/cmd_verify.go:212
github.com/Southclaws/cj/bot/commands.(*CommandManager).commandVerify
	/cj/bot/commands/cmd_verify.go:61
github.com/Southclaws/cj/bot/commands.(*CommandManager).commandVerify-fm
	/cj/bot/commands/all_commands.go:22
github.com/Southclaws/cj/bot/commands.(*CommandManager).OnMessage
	/cj/bot/commands/command_manager.go:135
github.com/Southclaws/cj/bot.(*App).onMessage
	/cj/bot/discord.go:117
github.com/Southclaws/cj/bot.(*App).onMessage-fm
	/cj/bot/discord.go:33
github.com/bwmarrin/discordgo.messageCreateEventHandler.Handle
	/go/pkg/mod/github.com/bwmarrin/discordgo@v0.19.0/eventhandlers.go:497
runtime.goexit
	/usr/local/go/src/runtime/asm_amd64.s:1333

failed to get HTML root for user page
github.com/Southclaws/cj/forum.(*ForumClient).GetUserProfilePage
	/cj/forum/forum.go:55
github.com/Southclaws/cj/bot/commands.(*CommandManager).UserConfirmsProfile
	/cj/bot/commands/cmd_verify.go:212
github.com/Southclaws/cj/bot/commands.(*CommandManager).commandVerify
	/cj/bot/commands/cmd_verify.go:61
github.com/Southclaws/cj/bot/commands.(*CommandManager).commandVerify-fm
	/cj/bot/commands/all_commands.go:22
github.com/Southclaws/cj/bot/commands.(*CommandManager).OnMessage
	/cj/bot/commands/command_manager.go:135
github.com/Southclaws/cj/bot.(*App).onMessage
	/cj/bot/discord.go:117
github.com/Southclaws/cj/bot.(*App).onMessage-fm
	/cj/bot/discord.go:33
github.com/bwmarrin/discordgo.messageCreateEventHandler.Handle
	/go/pkg/mod/github.com/bwmarrin/discordgo@v0.19.0/eventhandlers.go:497
runtime.goexit
	/usr/local/go/src/runtime/asm_amd64.s:1333
github.com/Southclaws/cj/bot.(*App).onMessage
	/cj/bot/discord.go:119
github.com/Southclaws/cj/bot.(*App).onMessage-fm
	/cj/bot/discord.go:33
github.com/bwmarrin/discordgo.messageCreateEventHandler.Handle
	/go/pkg/mod/github.com/bwmarrin/discordgo@v0.19.0/eventhandlers.go:497

Utilise heartbeat message

The heartbeat is a function that fires every X minutes, my plan was to grab some context of the current conversation (if any) and try to generate a fun message related to that conversation - no crazy analytics, just a little keyword lookup with some contextual preset message types such as bringing up forum stats for a user if people are talking about the forum or stats or post count.

Another use for this would be to remind users of bot functionality, with a list of settable messages such as "If you are unverified, please verify your account by messaging 'verify' to me!" or "You can verify a user's SA-MP forum account by typing /verify @discord_username" etc.

Commands as classes

We should organize all commands on classes and handle them dynamically in a function when a message is received. For example every command class should be like this

type CommandName struct {}

func (c *CommandName) Name() string {
	return "command_name"
}

func (c *CommandName) Description() string {
	return "description"
}

func (c *CommandName) Usage() string {
	return "usage"
}

func (c *PollCommand) Process() {
}

Command rate limiting

Commands should be rate limited in:

  • private
  • channel
  • summon

This can either be per-user or global but generally follows this structure:

  1. When a message is received, mark the time it was received.
  2. When a message is received, check if the time since the last message is below a certain threshold
    • (per user) Increment a map[string]int value for the command author
    • (global) Increment an int for the bot
  3. If that value goes over a threshold, block commands for the next X seconds and reset the counters.

Account verification - Block new forum accounts

We had a problem today with some toxic users, 'general' channel was set for only verified members (atm), but some of these users have new fresh accounts and got verified rank. The idea is block very new forum accounts, maybe 1 month be enough. It will prevent toxic users.

Random quotes

Since the bot is logging all chat, a common IRC feature will be easy to implement: random quote from a user from the past. These kinds of things should be on a cooldown or require some kind of currency to use however to prevent spam.

Implement a /stats command which queries masterlist v2 for server statistics

Should be quite easy for anyone who has used RESTful APIs before:

  • use Resty to perform a GET request
  • hit http://api.samp.southcla.ws/v2/stats (dependent on Southclaws/samp-servers-api#9)
  • import the data structure for the response from Southclaws/announce-backend
  • decode, format and send to the channel

This feature depends on the v2 milestone being completed, merged and deployed: https://github.com/Southclaws/announce-backend/milestones/1

Per-user sentiment analysis

Another fun feature to see how "salty" different people get!

Just like the Markov generator, using Spacy/Textacy/NLTK, an external app will calculate an average phrase sentiment for each user and expose a command to the primary channel where users can query other user's sentiment to see how much of what they say is perceived as positive or negative.

Announcing forum releases

When a new thread is posted in the includes, filterscripts, or plugins section CJ can announce it in #releases

I think it'd be nice to have this but it should be restricted somehow... Perhaps only for people with a certain amount of rep (nothing big)? I just don't think it'd be right to announce ALL releases...

Command for wiki searches

Hey man, I did a suggestion for CJ bot but idk if you saw it.
The suggestion is create a /wiki command and make the bot return a link to http://wiki.sa-mp.com/wiki/Parameter
E.g.:
(user command)
/wiki onplayerd

(bot messages, limit of matches is 3)
NPC:OnPlayerDeath http://wiki.sa-mp.com/wiki/NPC:OnPlayerDeath
OnPlayerDeath http://wiki.sa-mp.com/wiki/OnPlayerDeath
OnPlayerDisconnect http://wiki.sa-mp.com/wiki/OnPlayerDisconnect

I did an example of how it should work (I know you can do it, I'm using it to learn Go by the first time!):
https://gist.github.com/Renato-Garcia/97c62f4b55db3d2bcfceddbda87710d1

cc @Renato-Garcia

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.