diff options
| author | Arslaan Pathan <[email protected]> | 2026-07-09 17:58:29 +1200 |
|---|---|---|
| committer | Arslaan Pathan <[email protected]> | 2026-07-09 17:58:29 +1200 |
| commit | 8dea6c48621b6f933a3a88fbffa4277bda8c00c5 (patch) | |
| tree | cd576333e906dc210da067809033a4ad9c6d2a67 /src | |
| parent | d9499138aeaf32faee65640e624719df83f465eb (diff) | |
| download | slack-botnet-8dea6c48621b6f933a3a88fbffa4277bda8c00c5.tar.xz slack-botnet-8dea6c48621b6f933a3a88fbffa4277bda8c00c5.zip | |
Botnet deployments work!
:yayayayayay-ctp:
Diffstat (limited to 'src')
| -rw-r--r-- | src/botnet.py | 67 | ||||
| -rw-r--r-- | src/db.py | 99 | ||||
| -rw-r--r-- | src/main.py | 76 |
3 files changed, 210 insertions, 32 deletions
diff --git a/src/botnet.py b/src/botnet.py new file mode 100644 index 0000000..46b59d5 --- /dev/null +++ b/src/botnet.py @@ -0,0 +1,67 @@ +from .db import create_deployment, get_daily_user_deployment_count, get_deployment, update_deployment_status, get_active_users, get_user, add_deployment_message, get_deployment_messages +from slack_sdk.web.async_client import AsyncWebClient +import os +from dotenv import load_dotenv + +DAILY_LIMIT = 2 + +load_dotenv() +selfbot_token = os.environ.get("SLACK_SELFBOT_OAUTH_XOXP") + +async def deploy_message(user_id: str, message: str): + user = await get_user(user_id) + if not user: + return { + "success": False, + "error": "You must be part of the botnet to deploy messages!" + } + + daily_count = await get_daily_user_deployment_count(user_id) + if daily_count >= DAILY_LIMIT and user_id not in ["U0AKCBZHHMH"]: + return { + "success": False, + "error": f"You've reached your daily limit of {DAILY_LIMIT} messages!" + } + + deployment_id = await create_deployment(user_id, message) + + print(f"DEBUG: Executing the deployment {deployment_id} on arrival - REMOVE THIS LATER!") + await execute_deployment(deployment_id) + + return { + "success": True, + "deployment_id": deployment_id, + "message": "Your message has been deployed!" + } + +async def execute_deployment(deployment_id: int): + deployment = await get_deployment(deployment_id) + if not deployment: + return + + users = await get_active_users() + message = deployment["message"] + + for user in users: + try: + client = AsyncWebClient(token=user["oauth_token"]) + response = await client.chat_postMessage( + channel="C0BGMV1CAQG", # put it in #botnet for now, make it detect recent thread with CM approval yada yada later + text=message, + as_user=True + ) + await add_deployment_message(deployment_id=deployment_id, user_id=user["slack_id"], channel_id="C0BGMV1CAQG", message_id=response["ts"]) + except Exception as e: + print(f"Failed to send #{deployment_id} on behalf of {user['slack_id']}: {e}") + + try: + client = AsyncWebClient(token=selfbot_token) + await client.chat_postMessage( + channel="C0BGMV1CAQG", + text=f"Deployment #{deployment_id} deployed.\n\nMessage: {message}", + as_user=True + ) + except Exception as e: + print(f"Failed to send deployment status message: {e}") + + await update_deployment_status(deployment_id, "deployed") @@ -1,4 +1,5 @@ from slack_sdk.web.async_client import AsyncWebClient +from datetime import datetime import aiosqlite DB_PATH = "botnet.db" @@ -12,6 +13,28 @@ async def init_db(): joined_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) """) + await db.execute(""" + CREATE TABLE IF NOT EXISTS Deployments ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + message TEXT NOT NULL, + submitted_by TEXT NOT NULL, + status TEXT DEFAULT 'pending', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + deployed_at TIMESTAMP + ) + """) + await db.execute(""" + CREATE TABLE IF NOT EXISTS DeploymentMessages ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + deployment_id INTEGER NOT NULL, + user_id TEXT NOT NULL, + message_id TEXT NOT NULL, + channel_id TEXT NOT NULL, + sent_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (deployment_id) REFERENCES Deployments(id) ON DELETE CASCADE, + FOREIGN KEY (user_id) REFERENCES Users(slack_id) + ) + """) await db.commit() async def add_user(slack_id: str, oauth_token: str): @@ -30,3 +53,79 @@ async def add_user(slack_id: str, oauth_token: str): ) except Exception as e: print(f"Failed to send welcome message: {e}") + +async def create_deployment(user_id: str, message: str) -> int: + async with aiosqlite.connect(DB_PATH) as db: + cursor = await db.execute( + "INSERT INTO Deployments (message, submitted_by) VALUES (?, ?)", + (message, user_id) + ) + await db.commit() + return cursor.lastrowid + +async def get_daily_user_deployment_count(user_id: str) -> int: + from datetime import datetime + today = datetime.now().date().isoformat() + async with aiosqlite.connect(DB_PATH) as db: + cursor = await db.execute( + """SELECT COUNT(*) FROM Deployments + WHERE submitted_by = ? AND date(created_at) = ?""", + (user_id, today) + ) + row = await cursor.fetchone() + return row[0] if row else 0 + +async def get_deployment(deployment_id: int) -> dict | None: + async with aiosqlite.connect(DB_PATH) as db: + db.row_factory = aiosqlite.Row + cursor = await db.execute( + "SELECT * FROM Deployments WHERE id = ?", + (deployment_id,) + ) + row = await cursor.fetchone() + return dict(row) if row else None + +async def update_deployment_status(deployment_id: int, status: str): + async with aiosqlite.connect(DB_PATH) as db: + await db.execute( + "UPDATE Deployments SET status = ? WHERE id = ?", + (status, deployment_id) + ) + await db.commit() + +async def add_deployment_message(deployment_id: int, user_id: str, channel_id: str, message_id: str): + async with aiosqlite.connect(DB_PATH) as db: + await db.execute( + """INSERT INTO DeploymentMessages + (deployment_id, user_id, channel_id, message_id) + VALUES (?, ?, ?, ?)""", + (deployment_id, user_id, channel_id, message_id) + ) + await db.commit() + +async def get_deployment_messages(deployment_id: int) -> list[dict]: + async with aiosqlite.connect(DB_PATH) as db: + db.row_factory = aiosqlite.Row + cursor = await db.execute( + "SELECT * FROM DeploymentMessages WHERE deployment_id = ?", + (deployment_id,) + ) + rows = await cursor.fetchall() + return [dict(row) for row in rows] + +async def get_user(slack_id: str) -> dict | None: + async with aiosqlite.connect(DB_PATH) as db: + db.row_factory = aiosqlite.Row + cursor = await db.execute( + "SELECT * FROM Users WHERE slack_id = ?", + (slack_id,) + ) + row = await cursor.fetchone() + return dict(row) if row else None + +async def get_active_users() -> list[dict]: + async with aiosqlite.connect(DB_PATH) as db: + db.row_factory = aiosqlite.Row + cursor = await db.execute("SELECT * FROM Users") + rows = await cursor.fetchall() + return [dict(row) for row in rows] diff --git a/src/main.py b/src/main.py index 8b0e692..02375cc 100644 --- a/src/main.py +++ b/src/main.py @@ -4,6 +4,7 @@ from slack_bolt.adapter.socket_mode.async_handler import AsyncSocketModeHandler from dotenv import load_dotenv from .db import init_db from .auth import app as fastapi_app +from .botnet import deploy_message import threading import asyncio import uvicorn @@ -18,39 +19,50 @@ app = AsyncApp(token=selfbot_token) @app.message("") async def handle_messages(message, say): - if message.get("channel_type") != "im": - return - - if message.get("user") == selfbot_user_id: - return + try: + if message.get("channel_type") != "im": + return + + if message.get("user") == selfbot_user_id: + return - if message.get("user") != "U0AKCBZHHMH": - await say("Botnet is temporarily disabled for users other than <@U0AKCBZHHMH> for debugging and the safety of all OAuth-authorized users. Please DM <@U0AKCBZHHMH> if this is not resolved within a few days.") - return - - text = message.get("text", "").lower().strip() - - if not text.startswith("botnet"): - # yay more scuffed arbitrary stuff - text = "botnet help" - - parts = text.split(" ") - if len(parts) == 1: - # scuffed as hell, just sets it to help arbitrarily so it gets caught later LMAO - # too bad, if it works dont touch it - parts = ['botnet', 'help'] - - command = parts[1] - args = parts[2:] - - if command == "help": - await say("*Botnet Commands*:\n---\n*botnet help*: shows this message\n*botnet deploy <message>*: sends a message into the deploy queue for review/voting\n*botnet approve <channel>*: approve Botnet to function in a channel (must be CM)", mrkdwn=True) - elif command == "deploy": - await say("Not implemented yet") - elif command == "approve": - await say("Not implemented yet") - else: - await say(f"Unknown command: {command}") + if message.get("user") not in ["U0AKCBZHHMH", "U0BG82V0CP6"]: + await say("Botnet is temporarily disabled for users other than <@U0AKCBZHHMH> for debugging and the safety of all OAuth-authorized users. Please DM <@U0AKCBZHHMH> if this is not resolved within a few days.") + return + + text = message.get("text", "").strip() + + if not text.startswith("botnet"): + # yay more scuffed arbitrary stuff + text = "botnet help" + + parts = text.split(" ") + if len(parts) == 1: + # scuffed as hell, just sets it to help arbitrarily so it gets caught later LMAO + # too bad, if it works dont touch it + parts = ['botnet', 'help'] + + command = parts[1] + args = parts[2:] + + if command == "help": + await say("*Botnet Commands*:\n---\n*botnet help*: shows this message\n*botnet deploy <message>*: sends a message into the deploy queue for review/voting\n*botnet approve <channel>*: approve Botnet to function in a channel (must be CM)", mrkdwn=True) + elif command == "deploy": + if not args: + await say("Please provide a message to deploy!") + return + message_text = " ".join(args) + result = await deploy_message(message.get("user"), message_text) + if result["success"]: + await say(f"^_^ {result['message']} (Deployment #{result['deployment_id']})") + else: + await say(f"T_T {result['error']}") + elif command == "approve": + await say("Not implemented yet") + else: + await say(f"Unknown command: {command}") + except Exception as e: + print(f"something broke, good luck LMAO {e}") @app.event("message") async def shut_up_slack_bolt(): |
