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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
|
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, approve_channel, is_channel_approved, revoke_channel_approval
from .utils import generate_voting_blocks, is_user_cm
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)\n*botnet revoke <channel>*: revoke approval for Botnet to unction in a channel (must be CM)\n*botnet oauth*: sends you an OAuth link to join the botnet", 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":
user_id = message.get("user")
if not args:
await say(f"Please provide a channel to approve!")
return
channel_pattern = re.compile(r"^<#([A-Z0-9]+)(?:\|[^>]+)?>$")
match = channel_pattern.match(args[0].strip())
channel_id = match.group(1)
if not channel_id:
await say(f"Please provide a valid channel.")
return
if not await is_user_cm(channel_id, user_id):
await say(f"You must be a CM of that channel to perform this action!")
return
await approve_channel(channel_id, user_id)
await say(f"^_^ Approved channel {args[0].strip()}!")
elif command == "revoke":
user_id = message.get("user")
if not args:
await say(f"Please provide a channel to revoke approval!")
return
channel_pattern = re.compile(r"^<#([A-Z0-9]+)(?:\|[^>]+)?>$")
match = channel_pattern.match(args[0].strip())
channel_id = match.group(1)
if not channel_id:
await say(f"Please provide a valid channel.")
return
if not await is_channel_approved(channel_id):
await say(f"T_T Channel {args[0].strip()} is not currently approved for Botnet!")
return
if not await is_user_cm(channel_id, user_id):
await say(f"You must be a CM of that channel to perform this action!")
return
await revoke_channel_approval(channel_id)
await say(f"T_T Channel {args[0].strip()} has been revoked approval for Botnet!")
elif command == "oauth":
await say(f"{os.environ.get('SLAKC_OAUTH_PREFIX')}/slack/oauth/install")
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
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())
|