Compare commits

..

1 Commits

Author SHA1 Message Date
bot
49ea9d1359 Update export statistics 2024-11-27 09:57:53 +00:00
11 changed files with 55 additions and 41 deletions

Binary file not shown.

Binary file not shown.

View File

@ -7,6 +7,33 @@ Author-email: retoor@molodetz.nl
License: MIT License: MIT
Requires-Python: >=3.7 Requires-Python: >=3.7
Description-Content-Type: text/markdown Description-Content-Type: text/markdown
Requires-Dist: aiohttp==3.10.10
Requires-Dist: dataset==1.6.2
Requires-Dist: requests==2.32.3 Requires-Dist: requests==2.32.3
# Ragnar
This is an anti spam bot network. It is named after the viking for no obvious reason.
I'm not happy about the quality of the source and it is not a representation of my usual work. If I would've spend more efford there would be some types and I've would use aiohttp and would've used context managers for example. Despite the source lacking a certain quality, the bots work great and are made not to be annoying to the server by not connecting all at once and caching certain things like user profile / user id and if a reand already is flaged for example to not annoy the server.
The bots have user name no-spam[1-4] but flag under a Russian girl name, also for no obvious reason. I liked it more than some technical name. Will probably rename the bots later. Could be that devRants prevents me to do that within a half year. It doesn't matter much, if the bots do a good job, we will barely see them.
I expect this project tomorrow to have deployed fully functional on a server.
## In progress
The bots work perfect in sense that they're doing what they're programmed to do.
But the programming is not finished yet:
- the criteria can be better, tips how to optimize are very welcome.
- at this moment, they can only flag, useless, but we will have indication of future content to be cancelled. Every spam message should have a flag. If not, contact @retoor.
- the downvote function doesn't work because I couldn't figure out what value I had to post. Who knows it? After this, it's kinda done.
- a decent deployment on my server. Now it runs on my laptop because it's not done yet and it got late.
## How they work
One process starts four bots named no-spam[1-4]. These bots look at new rants.
If there is a new rant:
1. check if user has more than five posts. If so, it will not be seen as spam.
2. it will check certain keywords like hacker / money crypto related if so continue to step 3.
3. user will be informed by the bots that his rant is flagged and what to do about it.
4. rant will be downvoted by the four bots making it disappear.

View File

@ -1,3 +1,4 @@
README.md
pyproject.toml pyproject.toml
setup.cfg setup.cfg
src/Ragnar.egg-info/PKG-INFO src/Ragnar.egg-info/PKG-INFO
@ -11,4 +12,6 @@ src/ragnar/__main__.py
src/ragnar/api.py src/ragnar/api.py
src/ragnar/bot.py src/ragnar/bot.py
src/ragnar/cache.py src/ragnar/cache.py
src/ragnar/cli.py src/ragnar/cli.py
src/ragnar/tests/__init__.py
src/ragnar/tests/bot.py

View File

@ -1,3 +1 @@
aiohttp==3.10.10
dataset==1.6.2
requests==2.32.3 requests==2.32.3

View File

@ -1,12 +0,0 @@
import logging
import sys
logging.basicConfig(
level=logging.DEBUG,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
handlers=[
logging.StreamHandler(sys.stdout),
]
)
log = logging.getLogger(__name__)

View File

@ -1,7 +1,7 @@
import requests, json import requests, json
from ragnar.cache import method_cache from ragnar.cache import method_cache
from ragnar import log
class Api: class Api:
@ -27,7 +27,7 @@ class Api:
@method_cache @method_cache
def login(self): def login(self):
log.info("Logged in as {}".format(self.username)) print("New login, cache miss?")
rawdata = requests.post( rawdata = requests.post(
self.base_url + "users/auth-token", self.base_url + "users/auth-token",
data={"username": self.username, "password": self.password, "app": 3}, data={"username": self.username, "password": self.password, "app": 3},

View File

@ -3,7 +3,7 @@ import time
import random import random
from ragnar.cache import method_cache from ragnar.cache import method_cache
import re import re
from ragnar import log
class Bot: class Bot:
@ -11,7 +11,7 @@ class Bot:
self.username = username self.username = username
self.password = password self.password = password
self.name = self.username.split("@")[0] self.name = self.username.split("@")[0]
self.rant_history = []
names = { names = {
"no-spam": "anna", "no-spam": "anna",
"no-spam1": "ira", "no-spam1": "ira",
@ -24,14 +24,16 @@ class Bot:
self.name self.name
) )
self.auth = None self.auth = None
self.triggers = [ self.triggers = [
"$", "$",
"crypto", "crypto",
"hacker", "hacker",
"recovery", "recovery",
{"regex": r"\([+,(,0-9,),-]{7,}"}, {"regex": "\([+,(,0-9,),-]{7,}"},
"money", "money",
] ]
self.api = Api(username=self.username, password=self.password) self.api = Api(username=self.username, password=self.password)
def rsleepii(self): def rsleepii(self):
@ -42,14 +44,14 @@ class Bot:
self.rsleepii() self.rsleepii()
self.auth = self.api.login() self.auth = self.api.login()
if not self.auth: if not self.auth:
log.error("Authentication for {} failed.".format(self.username)) print("Authentication for {} failed.".format(self.username))
raise Exception("Login error") raise Exception("Login error")
log.info("Authentication succesful for {}.".format(self.username)) print("Authentication succesful for {}.".format(self.username))
def clean_rant_text(self, rant_text): def clean_rant_text(self, rant_text):
return rant_text.replace(" ", "").lower() return rant_text.replace(" ", "").lower()
@method_cache # @method_cache
def is_sus_rant(self, rant_id, rant_text): def is_sus_rant(self, rant_id, rant_text):
clean_text = self.clean_rant_text(rant_text) clean_text = self.clean_rant_text(rant_text)
for trigger in self.triggers: for trigger in self.triggers:
@ -57,10 +59,10 @@ class Bot:
if trigger.get("regex"): if trigger.get("regex"):
regex = trigger["regex"] regex = trigger["regex"]
if re.search(regex, clean_text): if re.search(regex, clean_text):
log.info("Regex trigger {} matched!".format(regex)) print("Regex trigger {} matched!".format(regex))
return True return True
elif trigger in clean_text: elif trigger in clean_text:
log.info("Trigger {} matched!".format(trigger)) print("Trigger {} matched!".format(trigger))
return True return True
return False return False
@ -80,7 +82,7 @@ class Bot:
profile = self.api.get_profile(user_id) profile = self.api.get_profile(user_id)
score = profile["score"] score = profile["score"]
if score < 5: if score < 5:
log.warning("User {} is sus with his score of only {}.".format(username, score)) print("User {} is sus with his score of only {}.".format(username, score))
return True return True
else: else:
return False return False
@ -93,24 +95,19 @@ class Bot:
self.rsleepii() self.rsleepii()
rants = self.api.get_rants("recent", 5, 0) rants = self.api.get_rants("recent", 5, 0)
for rant in rants: for rant in rants:
if rant['id'] in self.rant_history:
log.debug("{}: Already checked rant {}.".format(self.name,rant['id']))
continue
else:
self.rant_history.append(rant['id'])
if not self.is_user_sus(rant["user_username"]): if not self.is_user_sus(rant["user_username"]):
log.info("{}: User {} is trusted.".format(self.name, rant["user_username"])) print("User {} is trusted.".format(rant["user_username"]))
continue continue
if not self.is_sus_rant(rant["id"], rant["text"]): if not self.is_sus_rant(rant["id"], rant["text"]):
log.info("{}: Rant by {} is not sus.".format(self.name, rant["user_username"])) print("Rant by {} is not sus.".format(rant["user_username"]))
continue continue
if self.is_flagged_as_sus(rant["id"], rant.get("num_comments")): if self.is_flagged_as_sus(rant["id"], rant.get("num_comments")):
continue continue
log.warning("{}: Rant is not {} flagged as sus yet.".format(self.name,rant["user_username"])) print("Rant is not {} flagged as sus yet.".format(rant["user_username"]))
log.warning("{}: Flagging rant by {} as sus.".format(self.name, rant["user_username"])) print("Flagging rant by {} as sus.".format(rant["user_username"]))
self.mark_as_sus(rant) self.mark_as_sus(rant)
self.down_vote_rant(rant) self.down_vote_rant(rant)
def down_vote_rant(self, rant): def down_vote_rant(self, rant):
log.warning("Downvoting rant by {}.".format(rant["user_username"])) print("Downvoting rant by {}.".format(rant["user_username"]))
log.debug(self.api.post_rant_vote(rant["id"], 4)) print(self.api.post_rant_vote(rant["id"], 4))

View File

@ -3,7 +3,7 @@ from ragnar.bot import Bot
import random import random
import time import time
from concurrent.futures import ThreadPoolExecutor as Executor from concurrent.futures import ThreadPoolExecutor as Executor
from ragnar import log
def parse_args(): def parse_args():
parser = argparse.ArgumentParser(description="Process username and password.") parser = argparse.ArgumentParser(description="Process username and password.")
@ -15,7 +15,6 @@ def parse_args():
def bot_task(username, password): def bot_task(username, password):
log.info("Created new bot runniner. Username: {}".format(username))
time.sleep(random.randint(1, 20)) time.sleep(random.randint(1, 20))
bot = Bot(username=username, password=password) bot = Bot(username=username, password=password)
bot.login() bot.login()
@ -33,6 +32,8 @@ def main():
for x in range(1, 5): for x in range(1, 5):
username = "no-spam{}@molodetz.nl".format(str(x)) username = "no-spam{}@molodetz.nl".format(str(x))
password = args.password password = args.password
time.sleep(1)
print("Starting bot {}.".format(username))
executor.submit(bot_task, username, password) executor.submit(bot_task, username, password)
executor.shutdown(wait=True) executor.shutdown(wait=True)

Binary file not shown.

Binary file not shown.