aboutsummaryrefslogtreecommitdiff
path: root/src/main.py
blob: 0d63190043acb3704e1409ec5ee01f51629421d1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
import os
import re
import math
from slack_bolt.app.async_app import AsyncApp
from slack_bolt.adapter.socket_mode.async_handler import AsyncSocketModeHandler
from dotenv import load_dotenv
from .db import init_db, get_user, add_vote, get_vote_counts, get_user_vote, get_deployment, update_deployment_status, get_active_users
from .utils import generate_voting_blocks
from .auth import app as fastapi_app
from .botnet import deploy_message, execute_deployment
import threading
import asyncio
import uvicorn

load_dotenv()

VOTE_PATTERN = re.compile(r"^vote_(approve|reject)_(\d+)$")

selfbot_token = os.environ.get("SLACK_SELFBOT_OAUTH_XOXP")
app_token = os.environ.get("SLACK_APP_TOKEN")
selfbot_user_id = os.environ.get("SELFBOT_USER_ID")

app = AsyncApp(token=selfbot_token)

@app.message("")
async def handle_messages(message, say):
    try:
        if message.get("channel_type") != "im":
            return
        
        if message.get("user") == selfbot_user_id:
            return

        if message.get("user") not in ["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", "").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.action(VOTE_PATTERN)
async def handle_vote(ack, respond, logger, context, body, client):
    await ack()

    action_id = body["actions"][0]["action_id"]
    match = VOTE_PATTERN.match(action_id)

    if not match:
        return

    vote_type = match.group(1)
    deployment_id = int(match.group(2))

    user_id = body["user"]["id"]
    channel_id = body["channel"]["id"]
    user = await get_user(user_id)
    if not user:
        # await respond(f"You must join the botnet first!", response_type="ephemeral")
        await client.chat_postEphemeral(channel=channel_id, user=user_id, text="You must join the botnet first!")
        return

    deployment = await get_deployment(deployment_id)
    if not deployment or deployment["status"] != "pending":
        await client.chat_postEphemeral(channel=channel_id,user=user_id,text="Voting for this deployment has expired!")

    await add_vote(deployment_id, user_id, vote_type)

    approve_count, reject_count = await get_vote_counts(deployment_id)

    active_users = await get_active_users()
    total_members = len(active_users)
    # THRESHOLD = math.ceil(max(3, min(15, int(total_members * 0.2))))  # min 3, max 15 - use 20% total members
    THRESHOLD = 2 # Debug hardcoded
    print(f"DEBUG: Threshold is {THRESHOLD}")

    if approve_count >= THRESHOLD:
        await update_deployment_status(deployment_id, "approved")
        await execute_deployment(deployment_id)

        blocks = generate_voting_blocks(deployment_id, deployment["message"], approve_count, reject_count, False)
        await respond(blocks=blocks,text="An error occurred rendering this text.")
        await client.chat_postMessage(channel=channel_id, text=f"Deployment #{deployment_id} was approved! ^_^") 
    elif reject_count >= THRESHOLD:
        await update_deployment_status(deployment_id, "rejected")

        blocks = generate_voting_blocks(deployment_id, deployment["message"], approve_count, reject_count, False)
        await respond(blocks=blocks,text="An error occurred rendering this text.")
        await client.chat_postMessage(channel=channel_id, text=f"Deployment #{deployment_id} was rejected! T_T")
    else:
        blocks = generate_voting_blocks(deployment_id, deployment["message"], approve_count, reject_count, True)
        await respond(blocks=blocks,text="An error occurred rendering this text.")

@app.event("message")
async def shut_up_slack_bolt():
    # shut up that annoying debug warning that this event is unhandled
    pass

async def main():
    await init_db()

    def run_uvicorn():
        uvicorn.run(fastapi_app, host="0.0.0.0", port=8000)

    thread = threading.Thread(target=run_uvicorn, daemon=True)
    thread.start()

    handler = AsyncSocketModeHandler(app, app_token=app_token)
    await handler.start_async()

if __name__ == "__main__":
    # handler = SocketModeHandler(app, app_token=app_token)
    # handler.start()
    asyncio.run(main())