Compare commits
38
Commits
main
..
bd9946a288
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bd9946a288 | ||
|
|
f7522ddeac | ||
|
|
66bf7aa9ba | ||
|
|
34e519ae06 | ||
|
|
3080e87d88 | ||
|
|
6f856ad857 | ||
|
|
4420b3a2d2 | ||
|
|
5cd0c3c8ea | ||
|
|
8ca2e2a85a | ||
|
|
b7c19293b6 | ||
|
|
74b1f31aa5 | ||
|
|
072dd2b5f6 | ||
|
|
c3f755c989 | ||
|
|
a7389f008b | ||
|
|
b13a233540 | ||
|
|
252e99d811 | ||
|
|
282b41c7e2 | ||
|
|
ce2e5519b0 | ||
|
|
77c57ae2af | ||
|
|
ad886372f6 | ||
|
|
d3180b43c2 | ||
|
|
4da1385dc5 | ||
|
|
b04bbd4514 | ||
|
|
e504d2cd0b | ||
|
|
bc2165c402 | ||
|
|
23efe6b7b9 | ||
|
|
067bc28720 | ||
|
|
6970401d14 | ||
|
|
fdf115b4e1 | ||
|
|
0482cfc0ec | ||
|
|
720ffc93ce | ||
|
|
d0d2db1136 | ||
|
|
a6c626cd08 | ||
|
|
ac851d76c3 | ||
|
|
dba78e616c | ||
|
|
8b4d553772 | ||
|
|
42eca170a6 | ||
|
|
c48bf54dc1 |
@@ -20,4 +20,3 @@ jobs:
|
|||||||
- run: git config --global user.name "bot"
|
- run: git config --global user.name "bot"
|
||||||
- run: git commit -a -m "Update export statistics"
|
- run: git commit -a -m "Update export statistics"
|
||||||
- run: git push
|
- run: git push
|
||||||
- run: echo "This job's status is ${{ job.status }}."
|
|
||||||
|
|||||||
+1
-4
@@ -1,7 +1,4 @@
|
|||||||
config.py
|
config.py
|
||||||
.venv
|
.venv
|
||||||
.history
|
.history
|
||||||
__pycache__/
|
src/ragnar/__pycache__
|
||||||
errors.log
|
|
||||||
ragnar.log
|
|
||||||
.build-trigger-2024-12-02 14:26
|
|
||||||
@@ -1,24 +1,21 @@
|
|||||||
ENV=./.venv/bin/activate
|
|
||||||
PYTHON=./.venv/bin/python
|
|
||||||
BIN=./.venv/bin
|
|
||||||
all: ensure_env format build install test
|
all: ensure_env format build install test
|
||||||
|
|
||||||
format:
|
format:
|
||||||
$(PYTHON) -m pip install shed
|
./.venv/bin/python -m pip install black
|
||||||
. $(ENV) && python -m shed
|
./.venv/bin/python -m black .
|
||||||
|
|
||||||
ensure_env:
|
ensure_env:
|
||||||
-@python3 -m venv .venv
|
-@python3 -m venv .venv
|
||||||
|
|
||||||
build:
|
build:
|
||||||
$(PYTHON) -m pip install build
|
./.venv/bin/python -m pip install build
|
||||||
$(PYTHON) -m build .
|
./.venv/bin/python -m build .
|
||||||
|
|
||||||
install:
|
install:
|
||||||
$(PYTHON) -m pip install -e .
|
./.venv/bin/python -m pip install -e .
|
||||||
|
|
||||||
run:
|
run:
|
||||||
$(BIN)/ragnar.run
|
python -m ragnar.run
|
||||||
|
|
||||||
test:
|
test:
|
||||||
$(PYTHON) -m unittest ragnar.tests
|
./.venv/bin/python -m unittest ragnar.tests
|
||||||
@@ -1,87 +1,28 @@
|
|||||||
# Ragnar Bot: Automated Moderation System for Devrant.io
|
# Ragnar
|
||||||
|
|
||||||
## Overview
|
This is an anti spam bot network. It is named after the viking for no obvious reason.
|
||||||
Ragnar Bot is an automated system designed to interact with the **Devrant.io API** for community moderation. It detects spam, flags inappropriate content, and takes corrective actions such as downvoting and commenting. The system is scalable, customizable, and supports concurrent operations for multiple users.
|
|
||||||
|
|
||||||
---
|
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.
|
||||||
|
|
||||||
## Features
|
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.
|
||||||
|
|
||||||
### API Integration
|
I expect this project tomorrow to have deployed fully functional on a server.
|
||||||
- **User Authentication**: Secure login with credentials.
|
|
||||||
- **Profile Retrieval**: Access user profile data.
|
|
||||||
- **Content Search**: Find posts matching specific keywords or patterns.
|
|
||||||
- **Comments Management**: Post and fetch comments on rants.
|
|
||||||
- **Voting System**: Upvote or downvote rants programmatically.
|
|
||||||
- **Fetch Rants**: Retrieve and sort recent rants for analysis.
|
|
||||||
|
|
||||||
### Spam Detection
|
## In progress
|
||||||
- **Keyword Triggers**: Detect spam using predefined keywords like `crypto`, `bitcoin`, and more.
|
|
||||||
- **Regex Matching**: Identify suspicious patterns in text, such as URLs or phone numbers.
|
|
||||||
- **Heuristic Checks**: Evaluate user profiles and comments for unusual behavior.
|
|
||||||
|
|
||||||
### Automation
|
The bots work perfect in sense that they're doing what they're programmed to do.
|
||||||
- **Flagging Spam**: Automatically downvote and comment on posts flagged as spam.
|
But the programming is not finished yet:
|
||||||
- **Concurrent Execution**: Operates multiple bots simultaneously using multithreading.
|
- the criteria can be better, tips how to optimize are very welcome.
|
||||||
- **Customizable Triggers**: Adjust detection patterns to fit evolving spam trends.
|
- 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.
|
||||||
|
|
||||||
### Logging & Caching
|
## How they work
|
||||||
- **Logging**: Outputs activity logs to both the console and a file for debugging and monitoring.
|
One process starts four bots named no-spam[1-4]. These bots look at new rants.
|
||||||
- **Caching**: Uses a custom caching system to optimize performance and reduce redundant operations.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## How It Works
|
|
||||||
|
|
||||||
### Components
|
|
||||||
1. **API**:
|
|
||||||
Handles all interactions with the Devrant.io platform, including user authentication, content retrieval, and posting actions.
|
|
||||||
|
|
||||||
2. **Bot**:
|
|
||||||
Implements moderation logic:
|
|
||||||
- Identifies suspicious content.
|
|
||||||
- Posts comments warning users about flagged content.
|
|
||||||
- Maintains a history of checked rants to avoid redundant processing.
|
|
||||||
|
|
||||||
3. **CLI**:
|
|
||||||
Command-line interface to configure and execute the bot. Accepts user credentials and runs bots for multiple users simultaneously.
|
|
||||||
|
|
||||||
4. **Victoria Integration**:
|
|
||||||
Fetches user-related data from a remote service (`victoria.molodetz.nl`) for enhanced operations.
|
|
||||||
|
|
||||||
### Workflow
|
|
||||||
1. The bot authenticates using the provided username and password.
|
|
||||||
2. It retrieves and evaluates recent rants for spam indicators.
|
|
||||||
3. If spam is detected:
|
|
||||||
- Posts a predefined warning comment.
|
|
||||||
- Downvotes the rant.
|
|
||||||
4. Maintains logs and runs continuously to monitor new rants.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Installation & Usage
|
|
||||||
|
|
||||||
1. **Setup**:
|
|
||||||
Clone the repository and ensure all dependencies are installed.
|
|
||||||
|
|
||||||
2. **Run the Bot**:
|
|
||||||
Execute the bot via the CLI:
|
|
||||||
```bash
|
|
||||||
python cli.py -u <username> -p <password>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Fan art
|
|
||||||
This is some fan art made by Buffon, a contributor of this project:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Another one:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Third one, the legendary bot war of 2024 ended in peace.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
2024-12-17 13:06:42,285 - __main__ - DEBUG - This is a debug message
|
|
||||||
2024-12-17 13:06:42,286 - __main__ - INFO - This is an info message
|
|
||||||
2024-12-17 13:06:42,286 - __main__ - WARNING - This is a warning message
|
|
||||||
2024-12-17 13:06:42,286 - __main__ - ERROR - This is an error message
|
|
||||||
2024-12-17 13:06:42,286 - __main__ - CRITICAL - This is a critical message
|
|
||||||
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 164 KiB |
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 168 KiB |
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 131 KiB |
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
-29
@@ -1,29 +0,0 @@
|
|||||||
import logging
|
|
||||||
|
|
||||||
# Create or get the root logger
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
logger.setLevel(logging.DEBUG) # Set the logging level
|
|
||||||
|
|
||||||
# Create a file handler
|
|
||||||
file_handler = logging.FileHandler("app.log")
|
|
||||||
file_handler.setLevel(logging.DEBUG) # Set the level for this handler
|
|
||||||
|
|
||||||
# Create a console handler (optional, for logging to the console too)
|
|
||||||
console_handler = logging.StreamHandler()
|
|
||||||
console_handler.setLevel(logging.INFO)
|
|
||||||
|
|
||||||
# Create a logging format
|
|
||||||
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
|
|
||||||
file_handler.setFormatter(formatter)
|
|
||||||
console_handler.setFormatter(formatter)
|
|
||||||
|
|
||||||
# Add the handlers to the logger
|
|
||||||
logger.addHandler(file_handler)
|
|
||||||
logger.addHandler(console_handler)
|
|
||||||
|
|
||||||
# Log some messages
|
|
||||||
logger.debug("This is a debug message")
|
|
||||||
logger.info("This is an info message")
|
|
||||||
logger.warning("This is a warning message")
|
|
||||||
logger.error("This is an error message")
|
|
||||||
logger.critical("This is a critical message")
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
Metadata-Version: 2.4
|
Metadata-Version: 2.1
|
||||||
Name: Ragnar
|
Name: Ragnar
|
||||||
Version: 1.3.37
|
Version: 1.3.37
|
||||||
Summary: Anti spam bot for dR
|
Summary: Anti spam bot for dR
|
||||||
@@ -9,90 +9,31 @@ Requires-Python: >=3.7
|
|||||||
Description-Content-Type: text/markdown
|
Description-Content-Type: text/markdown
|
||||||
Requires-Dist: requests==2.32.3
|
Requires-Dist: requests==2.32.3
|
||||||
|
|
||||||
# Ragnar Bot: Automated Moderation System for Devrant.io
|
# Ragnar
|
||||||
|
|
||||||
## Overview
|
This is an anti spam bot network. It is named after the viking for no obvious reason.
|
||||||
Ragnar Bot is an automated system designed to interact with the **Devrant.io API** for community moderation. It detects spam, flags inappropriate content, and takes corrective actions such as downvoting and commenting. The system is scalable, customizable, and supports concurrent operations for multiple users.
|
|
||||||
|
|
||||||
---
|
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.
|
||||||
|
|
||||||
## Features
|
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.
|
||||||
|
|
||||||
### API Integration
|
I expect this project tomorrow to have deployed fully functional on a server.
|
||||||
- **User Authentication**: Secure login with credentials.
|
|
||||||
- **Profile Retrieval**: Access user profile data.
|
|
||||||
- **Content Search**: Find posts matching specific keywords or patterns.
|
|
||||||
- **Comments Management**: Post and fetch comments on rants.
|
|
||||||
- **Voting System**: Upvote or downvote rants programmatically.
|
|
||||||
- **Fetch Rants**: Retrieve and sort recent rants for analysis.
|
|
||||||
|
|
||||||
### Spam Detection
|
## In progress
|
||||||
- **Keyword Triggers**: Detect spam using predefined keywords like `crypto`, `bitcoin`, and more.
|
|
||||||
- **Regex Matching**: Identify suspicious patterns in text, such as URLs or phone numbers.
|
|
||||||
- **Heuristic Checks**: Evaluate user profiles and comments for unusual behavior.
|
|
||||||
|
|
||||||
### Automation
|
The bots work perfect in sense that they're doing what they're programmed to do.
|
||||||
- **Flagging Spam**: Automatically downvote and comment on posts flagged as spam.
|
But the programming is not finished yet:
|
||||||
- **Concurrent Execution**: Operates multiple bots simultaneously using multithreading.
|
- the criteria can be better, tips how to optimize are very welcome.
|
||||||
- **Customizable Triggers**: Adjust detection patterns to fit evolving spam trends.
|
- 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.
|
||||||
|
|
||||||
### Logging & Caching
|
## How they work
|
||||||
- **Logging**: Outputs activity logs to both the console and a file for debugging and monitoring.
|
One process starts four bots named no-spam[1-4]. These bots look at new rants.
|
||||||
- **Caching**: Uses a custom caching system to optimize performance and reduce redundant operations.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## How It Works
|
|
||||||
|
|
||||||
### Components
|
|
||||||
1. **API**:
|
|
||||||
Handles all interactions with the Devrant.io platform, including user authentication, content retrieval, and posting actions.
|
|
||||||
|
|
||||||
2. **Bot**:
|
|
||||||
Implements moderation logic:
|
|
||||||
- Identifies suspicious content.
|
|
||||||
- Posts comments warning users about flagged content.
|
|
||||||
- Maintains a history of checked rants to avoid redundant processing.
|
|
||||||
|
|
||||||
3. **CLI**:
|
|
||||||
Command-line interface to configure and execute the bot. Accepts user credentials and runs bots for multiple users simultaneously.
|
|
||||||
|
|
||||||
4. **Victoria Integration**:
|
|
||||||
Fetches user-related data from a remote service (`victoria.molodetz.nl`) for enhanced operations.
|
|
||||||
|
|
||||||
### Workflow
|
|
||||||
1. The bot authenticates using the provided username and password.
|
|
||||||
2. It retrieves and evaluates recent rants for spam indicators.
|
|
||||||
3. If spam is detected:
|
|
||||||
- Posts a predefined warning comment.
|
|
||||||
- Downvotes the rant.
|
|
||||||
4. Maintains logs and runs continuously to monitor new rants.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Installation & Usage
|
|
||||||
|
|
||||||
1. **Setup**:
|
|
||||||
Clone the repository and ensure all dependencies are installed.
|
|
||||||
|
|
||||||
2. **Run the Bot**:
|
|
||||||
Execute the bot via the CLI:
|
|
||||||
```bash
|
|
||||||
python cli.py -u <username> -p <password>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Fan art
|
|
||||||
This is some fan art made by Buffon, a contributor of this project:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Another one:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Third one, the legendary bot war of 2024 ended in peace.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,5 @@ 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/victoria.py
|
|
||||||
src/ragnar/tests/__init__.py
|
src/ragnar/tests/__init__.py
|
||||||
src/ragnar/tests/bot.py
|
src/ragnar/tests/bot.py
|
||||||
@@ -1,13 +1,12 @@
|
|||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
file_handler = logging.FileHandler("ragnar.log")
|
|
||||||
file_handler.setLevel(logging.DEBUG)
|
|
||||||
|
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
level=logging.DEBUG,
|
level=logging.DEBUG,
|
||||||
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
||||||
handlers=[logging.StreamHandler(sys.stdout), file_handler],
|
handlers=[
|
||||||
|
logging.StreamHandler(sys.stdout),
|
||||||
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|||||||
+3
-5
@@ -1,9 +1,7 @@
|
|||||||
import json
|
import requests, json
|
||||||
|
|
||||||
import requests
|
|
||||||
|
|
||||||
from ragnar import log
|
|
||||||
from ragnar.cache import method_cache
|
from ragnar.cache import method_cache
|
||||||
|
from ragnar import log
|
||||||
|
|
||||||
|
|
||||||
class Api:
|
class Api:
|
||||||
@@ -30,7 +28,7 @@ class Api:
|
|||||||
|
|
||||||
@method_cache
|
@method_cache
|
||||||
def login(self):
|
def login(self):
|
||||||
log.info(f"Logged in as {self.username}")
|
log.info("Logged in as {}".format(self.username))
|
||||||
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},
|
||||||
|
|||||||
+36
-125
@@ -1,32 +1,31 @@
|
|||||||
import re
|
|
||||||
import time
|
|
||||||
|
|
||||||
from ragnar import log
|
|
||||||
from ragnar.api import Api
|
from ragnar.api import Api
|
||||||
|
import time
|
||||||
|
import random
|
||||||
from ragnar.cache import method_cache
|
from ragnar.cache import method_cache
|
||||||
from ragnar.victoria import vic
|
import re
|
||||||
|
from ragnar import log
|
||||||
|
import json
|
||||||
|
|
||||||
|
|
||||||
class Bot:
|
class Bot:
|
||||||
|
|
||||||
rant_history = []
|
|
||||||
|
|
||||||
def __init__(self, username, password):
|
def __init__(self, username, password):
|
||||||
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 = []
|
||||||
self.amount_of_rants_to_check = 30
|
self.amount_of_rants_to_check = 5
|
||||||
self.sus_users = vic.get_spammers()
|
names = {
|
||||||
self.names = {
|
|
||||||
"no-spam": "Anna",
|
"no-spam": "Anna",
|
||||||
"no-spam1": "Ira",
|
"no-spam1": "Ira",
|
||||||
"no-spam2": "Katya",
|
"no-spam2": "Katya",
|
||||||
"no-spam3": "Nastya",
|
"no-spam3": "Nastya",
|
||||||
"no-spam4": "Vira",
|
"no-spam4": "Vira",
|
||||||
}
|
}
|
||||||
self.name = self.names.get(self.name, self.username.split("@")[0])
|
self.name = names.get(self.name, "everyone")
|
||||||
self.mark_text = f"I am {self.name} and downvoted this post because I consider it spam. Your message will be removed from this community site due too much downvotes. See my profile for more information. Read my source code mentioned on my profile to see what you did wrong. Should be no problem for a developer.\n\nHave a nice day!\n\n\nIf your post is not spam, please mention @retoor."
|
self.mark_text = "I am {} and downvoted this post because post is considered spam. Your message will be removed from this community site due too much downvotes. See my profile for more information. Read my source code mentioned on my profile to see what you did wrong. Should be no problem for a developer.\n\nHave a nice day!\n\n\nIf the post is not spam, please mention @retoor in the comments of this rant.".format(
|
||||||
|
self.name
|
||||||
|
)
|
||||||
self.auth = None
|
self.auth = None
|
||||||
self.triggers = [
|
self.triggers = [
|
||||||
"$",
|
"$",
|
||||||
@@ -44,138 +43,65 @@ class Bot:
|
|||||||
"wizard",
|
"wizard",
|
||||||
"diamond",
|
"diamond",
|
||||||
"carat",
|
"carat",
|
||||||
"carats",
|
|
||||||
"leading",
|
|
||||||
"cheating",
|
|
||||||
"spouse",
|
|
||||||
"spy",
|
|
||||||
# "helping",
|
|
||||||
"unqiue",
|
|
||||||
"@ragnar",
|
|
||||||
"brides",
|
|
||||||
"singles",
|
|
||||||
"course",
|
|
||||||
"dating",
|
|
||||||
]
|
]
|
||||||
self.api = Api(username=self.username, password=self.password)
|
self.api = Api(username=self.username, password=self.password)
|
||||||
|
|
||||||
def rsleepii(self):
|
def rsleepii(self):
|
||||||
time.sleep(1)
|
time.sleep(random.randint(1, 3))
|
||||||
|
|
||||||
@method_cache
|
@method_cache
|
||||||
def login(self):
|
def login(self):
|
||||||
self.rsleepii()
|
self.rsleepii()
|
||||||
self.auth = self.api.login()
|
self.auth = self.api.login()
|
||||||
if not self.auth:
|
if not self.auth:
|
||||||
log.error(f"Authentication for {self.username} failed.")
|
log.error("Authentication for {} failed.".format(self.username))
|
||||||
raise Exception("Login error")
|
raise Exception("Login error")
|
||||||
log.info(f"Authentication succesful for {self.username}.")
|
log.info("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()
|
||||||
|
|
||||||
def get_mentions(self, rant_text):
|
|
||||||
return re.findall(r"@\w+", rant_text)
|
|
||||||
|
|
||||||
def is_comment_deletable(self, rant_id, username):
|
|
||||||
rant = self.api.get_rant(rant_id)
|
|
||||||
for comment in rant.get("comments", []):
|
|
||||||
print("Checking if sus comment: ", comment["body"])
|
|
||||||
if (
|
|
||||||
comment.get("user_username") == username
|
|
||||||
and comment.get("user_score") <= 10
|
|
||||||
):
|
|
||||||
vic.register_spammer(comment["user_username"])
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
@method_cache
|
@method_cache
|
||||||
def is_sus_content(self, content):
|
def is_sus_rant(self, rant_id, rant_text):
|
||||||
clean_text = self.clean_rant_text(content)
|
clean_text = self.clean_rant_text(rant_text)
|
||||||
for trigger in self.triggers:
|
for trigger in self.triggers:
|
||||||
if type(trigger) == dict:
|
if type(trigger) == dict:
|
||||||
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(f"Regex trigger {regex} matched!")
|
log.info("Regex trigger {} matched!".format(regex))
|
||||||
return True
|
return True
|
||||||
elif trigger in clean_text:
|
elif trigger in clean_text:
|
||||||
if trigger == "ragnar":
|
log.info("Trigger {} matched!".format(trigger))
|
||||||
mentions = self.get_mentions(content)
|
|
||||||
for mention in mentions:
|
|
||||||
if mention == "@ragnar":
|
|
||||||
continue
|
|
||||||
username = mention.strip("@")
|
|
||||||
if self.is_comment_deletable(username):
|
|
||||||
vic.register_spammer(mention.strip("@"))
|
|
||||||
self.api.post_comment(
|
|
||||||
rant_id,
|
|
||||||
f"User {username} is sucessfully registered as spammer.",
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
self.api.post_comment(
|
|
||||||
rant_id,
|
|
||||||
f"Can't register user {username} as spammer. User is trusted.",
|
|
||||||
)
|
|
||||||
if len(mentions) > 1:
|
|
||||||
return False
|
|
||||||
log.info(f"Trigger {trigger} matched!")
|
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@method_cache
|
|
||||||
def is_sus_rant(self, rant_id, rant_text):
|
|
||||||
return self.is_sus_content(rant_text)
|
|
||||||
|
|
||||||
def is_flagged_as_sus(self, rant_id, num_comments):
|
def is_flagged_as_sus(self, rant_id, num_comments):
|
||||||
if not num_comments:
|
if not num_comments:
|
||||||
return False
|
return False
|
||||||
self.rsleepii()
|
self.rsleepii()
|
||||||
rant = self.api.get_rant(rant_id)
|
rant = self.api.get_rant(rant_id)
|
||||||
for comment in rant.get("comments", []):
|
for comment in rant.get("comments", []):
|
||||||
if self.names.get(self.username, "") in comment.get("body", ""):
|
if self.mark_text in comment.get("body", ""):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@method_cache
|
@method_cache
|
||||||
def is_user_sus(self, username):
|
def is_user_sus(self, username):
|
||||||
if username in self.sus_users:
|
|
||||||
return True
|
|
||||||
user_id = self.api.get_user_id(username)
|
user_id = self.api.get_user_id(username)
|
||||||
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(f"User {username} is sus with his score of only {score}.")
|
log.warning(
|
||||||
|
"User {} is sus with his score of only {}.".format(username, score)
|
||||||
|
)
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def is_comments_sus(self, rant_id):
|
def mark_as_sus(self, rant):
|
||||||
log.info(f"Checking if comments are sus of rant {rant_id}.")
|
|
||||||
self.rsleepii()
|
self.rsleepii()
|
||||||
rant = self.api.get_rant(rant_id)
|
self.api.post_comment(rant["id"], self.mark_text)
|
||||||
for comment in rant.get("comments", []):
|
|
||||||
for spammer in self.sus_users:
|
|
||||||
if comment["user_username"] == spammer:
|
|
||||||
vic.downvote_comment(comment["id"])
|
|
||||||
return False
|
|
||||||
|
|
||||||
print("Checking if sus comment: ", comment["body"])
|
|
||||||
if "@ragnar" in comment.get("body", "").lower():
|
|
||||||
print("Ragnar is mentioned, so flagging as sus comment.")
|
|
||||||
return True
|
|
||||||
if comment["user_score"] >= 5:
|
|
||||||
print("User has reputation >= 5 so not sus.")
|
|
||||||
continue
|
|
||||||
if self.is_sus_content(comment.get("body", "")):
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def mark_as_sus(self, rant, is_flagged_by_ai):
|
|
||||||
mark_text = self.mark_text
|
|
||||||
if is_flagged_by_ai:
|
|
||||||
mark_text += "\n* Flagged by AI."
|
|
||||||
self.api.post_comment(rant["id"], mark_text)
|
|
||||||
|
|
||||||
def fight(self):
|
def fight(self):
|
||||||
self.rsleepii()
|
self.rsleepii()
|
||||||
@@ -184,37 +110,24 @@ class Bot:
|
|||||||
if rant["id"] in self.rant_history:
|
if rant["id"] in self.rant_history:
|
||||||
log.debug("{}: Already checked rant {}.".format(self.name, rant["id"]))
|
log.debug("{}: Already checked rant {}.".format(self.name, rant["id"]))
|
||||||
continue
|
continue
|
||||||
flagged_by_id = False
|
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(
|
log.info(
|
||||||
"{}: User {} is trusted.".format(self.name, rant["user_username"])
|
"{}: User {} is trusted.".format(self.name, rant["user_username"])
|
||||||
)
|
)
|
||||||
self.rant_history.append(rant["id"])
|
|
||||||
continue
|
continue
|
||||||
elif self.is_comments_sus(rant["id"]):
|
if not self.is_sus_rant(rant["id"], rant["text"]):
|
||||||
|
|
||||||
log.info("Comments of rant are sus + user sus. Will flag as spam.")
|
|
||||||
elif rant["user_username"] in self.sus_users:
|
|
||||||
pass
|
|
||||||
elif not self.is_sus_rant(rant["id"], rant["text"]):
|
|
||||||
if not self.is_user_sus(rant["user_username"]):
|
|
||||||
continue
|
|
||||||
log.info(
|
log.info(
|
||||||
"{}: Rant by {} is not sus by traditional method..".format(
|
"{}: Rant by {} is not sus.".format(
|
||||||
self.name, rant["user_username"]
|
self.name, rant["user_username"]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
if vic.is_spam(rant["text"]) <= 5:
|
continue
|
||||||
self.rant_history.append(rant["id"])
|
if self.is_flagged_as_sus(rant["id"], rant.get("num_comments")):
|
||||||
continue
|
continue
|
||||||
else:
|
|
||||||
log.info(
|
|
||||||
"{}: Rant by {} is sus according to AI.".format(
|
|
||||||
self.name, rant["user_username"]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
log.warning(
|
log.warning(
|
||||||
"{}: Rant by {} is not flagged as sus yet but should be.".format(
|
"{}: Rant is not {} flagged as sus yet.".format(
|
||||||
self.name, rant["user_username"]
|
self.name, rant["user_username"]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -223,11 +136,9 @@ class Bot:
|
|||||||
self.name, rant["user_username"]
|
self.name, rant["user_username"]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.mark_as_sus(rant, flagged_by_id)
|
self.mark_as_sus(rant)
|
||||||
self.down_vote_rant(rant)
|
self.down_vote_rant(rant)
|
||||||
self.rant_history.append(rant["id"])
|
|
||||||
|
|
||||||
def down_vote_rant(self, rant):
|
def down_vote_rant(self, rant):
|
||||||
vic.dr_downvote_rant(rant["id"], 5)
|
log.warning("Downvoting rant by {}.".format(rant["user_username"]))
|
||||||
log.warning("Downvoting rant by {} for 5 times.".format(rant["user_username"]))
|
log.debug(json.loads(self.api.post_rant_vote(rant["id"], -1)))
|
||||||
log.debug("Vote result: ".format(self.api.post_rant_vote(rant["id"], -1)))
|
|
||||||
|
|||||||
+23
-18
@@ -1,8 +1,9 @@
|
|||||||
import argparse
|
import argparse
|
||||||
import time
|
|
||||||
|
|
||||||
from ragnar import log
|
|
||||||
from ragnar.bot import Bot
|
from ragnar.bot import Bot
|
||||||
|
import random
|
||||||
|
import time
|
||||||
|
from concurrent.futures import ThreadPoolExecutor as Executor
|
||||||
|
from ragnar import log
|
||||||
|
|
||||||
|
|
||||||
def parse_args():
|
def parse_args():
|
||||||
@@ -14,23 +15,27 @@ def parse_args():
|
|||||||
return parser.parse_args()
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
|
def bot_task(username, password):
|
||||||
|
log.info("Created new bot runniner. Username: {}".format(username))
|
||||||
|
time.sleep(random.randint(1, 20))
|
||||||
|
bot = Bot(username=username, password=password)
|
||||||
|
bot.login()
|
||||||
|
while True:
|
||||||
|
time.sleep(random.randint(1, 20))
|
||||||
|
try:
|
||||||
|
bot.fight()
|
||||||
|
except Exception as ex:
|
||||||
|
print(ex)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
args = parse_args()
|
args = parse_args()
|
||||||
usernames = ["no-spam1", "no-spam2", "no-spam3", "no-spam4"]
|
with Executor(4) as executor:
|
||||||
|
for x in range(1, 5):
|
||||||
while True:
|
username = "no-spam{}@molodetz.nl".format(str(x))
|
||||||
for username in usernames:
|
password = args.password
|
||||||
time_start = time.time()
|
executor.submit(bot_task, username, password)
|
||||||
try:
|
executor.shutdown(wait=True)
|
||||||
log.info(f"Created new bot runner. Username: {username}")
|
|
||||||
bot = Bot(username=username, password=args.password)
|
|
||||||
bot.login()
|
|
||||||
bot.fight()
|
|
||||||
except Exception as ex:
|
|
||||||
log.critical(ex, exc_info=True)
|
|
||||||
time.sleep(1)
|
|
||||||
time_duration = time.time() - time_start
|
|
||||||
log.info(f"Bot {username} finished in {time_duration} seconds.")
|
|
||||||
|
|
||||||
|
|
||||||
def run():
|
def run():
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,5 +1,4 @@
|
|||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from ragnar.bot import Bot
|
from ragnar.bot import Bot
|
||||||
|
|
||||||
|
|
||||||
@@ -23,7 +22,3 @@ class BotTestCase(unittest.TestCase):
|
|||||||
def test_is_sus_rant_regex_url_match_http_or_www(self):
|
def test_is_sus_rant_regex_url_match_http_or_www(self):
|
||||||
rant_text = "http:// www . google . nl"
|
rant_text = "http:// www . google . nl"
|
||||||
self.assertTrue(self.bot.is_sus_rant(42, rant_text))
|
self.assertTrue(self.bot.is_sus_rant(42, rant_text))
|
||||||
|
|
||||||
def test_is_sus_content(self):
|
|
||||||
rant_text = "I completely understand the frustration—it’s such a hassle to manage all those details manually. I recently started using a platform called best ai trip planner https://easytrip.ai/ and it’s been a game-changer for my travel planning. This AI-powered service analyzes your trip requirements and gives you a comprehensive plan with the best transportation options. Whether you’re looking for the fastest way to travel, the most scenic routes, or something within a specific budget, EasyTrip.ai does all the heavy lifting for you. It’s also great for last-minute plans because it quickly pulls together all the available options!"
|
|
||||||
self.assertTrue(self.bot.is_sus_content(rant_text))
|
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
from xmlrpc.client import ServerProxy
|
|
||||||
|
|
||||||
vic = ServerProxy("https://victoria.molodetz.nl/rpc")
|
|
||||||
Reference in New Issue
Block a user