-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.py
242 lines (207 loc) · 6.93 KB
/
main.py
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
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
from asyncio import Future, ensure_future, get_event_loop, sleep
from logging import INFO, basicConfig, error
from typing import Any, Optional
from discord import (ApplicationContext, Bot, IntegrationType, Intents,
InteractionContextType, Permissions, User,
default_permissions, option)
from tortoise import Tortoise, connections
from tortoise.queryset import Q
from utils.checks import check_admin, check_allowed
from utils.config import Config
from utils.controller import Controller
from utils.models import Connection
basicConfig(
level=INFO,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
)
config = Config()
intents = Intents.default()
if config.expires:
intents.members = True
client = Bot(
intents=intents,
default_command_context={InteractionContextType.guild},
default_command_integration_types={IntegrationType.guild_install},
)
controller = Controller(client, config)
admin_group = client.create_group(
"admin",
"Admin commands for the Minecraft server.",
default_member_permissions=Permissions(administrator=True),
)
@client.slash_command(
name="minecraft",
description="Connect your Discord account to the Minecraft server.",
)
@option(
name="username",
description="Provide your Minecraft username.",
max_length=16,
type=str,
required=False,
)
async def minecraft(ctx: ApplicationContext, username: Optional[str]) -> Any:
if await check_allowed(ctx, config):
return
await ctx.defer(ephemeral=True)
if username is None:
return await controller.whitelist_remove(
ctx, ctx.author, "Your Discord account connection has been removed."
)
await controller.whitelist_add(ctx, username)
@admin_group.command(name="check", description="Check the user data.")
@option(name="user", description="Provide Discord user.", type=User, required=False)
@option(
name="username",
description="Provide Minecraft username.",
max_length=16,
type=str,
required=False,
)
async def check(
ctx: ApplicationContext, user: Optional[User], username: Optional[str]
) -> Any:
if await check_admin(ctx, user, username):
return
await ctx.defer(ephemeral=True)
await controller.user_check(ctx, user or username)
@admin_group.command(name="ban", description="Ban user from the server.")
@option(name="user", description="Provide Discord user.", type=User, required=False)
@option(
name="username",
description="Provide Minecraft username.",
max_length=16,
type=str,
required=False,
)
@option(
name="reason",
description="Reason for the ban.",
max_length=64,
type=str,
required=False,
)
async def ban(
ctx: ApplicationContext,
user: Optional[User],
username: Optional[str],
reason: Optional[str],
) -> Any:
if await check_admin(ctx, user, username):
return
await ctx.defer(ephemeral=True)
await controller.user_ban(ctx, user or username, reason)
@admin_group.command(name="unban", description="Ban user from the server.")
@option(name="user", description="Provide Discord user.", type=User, required=False)
@option(
name="username",
description="Provide Minecraft username.",
max_length=16,
type=str,
required=False,
)
async def unban(
ctx: ApplicationContext,
user: Optional[User],
username: Optional[str],
) -> Any:
if await check_admin(ctx, user, username):
return
await ctx.defer(ephemeral=True)
await controller.user_unban(ctx, user or username)
@admin_group.command(name="remove", description="Remove user from the whitelist.")
@option(name="user", description="Provide Discord user.", type=User, required=False)
@option(
name="username",
description="Provide Minecraft username.",
max_length=16,
type=str,
required=False,
)
@option(
name="reason",
description="Reason for the removal.",
max_length=64,
type=str,
required=False,
)
@default_permissions(administrator=True)
async def remove(
ctx: ApplicationContext,
user: Optional[User],
username: Optional[str],
reason: Optional[str],
) -> Any:
if await check_admin(ctx, user, username):
return
await ctx.defer(ephemeral=True)
await controller.whitelist_remove(ctx, user or username, reason)
@admin_group.command(
name="restart", description="Restart connection to the Minecraft server."
)
async def restart(
ctx: ApplicationContext,
) -> Any:
await ctx.defer(ephemeral=True)
await controller.connect()
await ctx.respond("🔄 Connection restarted!")
if config.admin_commands:
@client.slash_command(name="root", description="Execute any Minecraft command.")
@option(
name="command",
description="Provide the command you want to execute.",
max_length=256,
type=str,
)
@default_permissions(administrator=True)
async def root(ctx: ApplicationContext, command: str) -> Any:
await ctx.defer(ephemeral=True)
await controller.command(ctx, command)
expire_future: Optional[Future] = None
if config.expires and config.guild is not None:
async def expire_check() -> None:
while True:
if guild := client.get_guild(config.guild):
for connection in await Connection.filter(
~Q(username=None) & Q(is_banned=False)
):
member = guild.get_member(connection.user_id)
if not member or (
config.allowed_roles
and not any(
[role.id in config.allowed_roles for role in member.roles]
)
):
try:
await controller.whitelist_remove(
None,
connection.username,
"Membership has expired. Access has been revoked.",
)
except Exception as e:
error(f"Error in expire check: {e}")
await sleep(config.check_interval)
@client.listen("on_ready", once=True)
async def on_ready() -> None:
global expire_future
expire_future = ensure_future(expire_check())
async def start() -> None:
await Tortoise.init(db_url="sqlite://main.db", modules={"models": ["utils.models"]})
await Tortoise.generate_schemas()
await controller.connect()
await client.start(config.bot_token)
loop = get_event_loop()
try:
loop.run_until_complete(start())
except (KeyboardInterrupt, Exception) as e:
if isinstance(e, Exception):
error(f"Error in main loop: {e}")
if expire_future:
expire_future.cancel()
if not client.is_closed():
loop.run_until_complete(client.close())
loop.run_until_complete(controller.close())
loop.run_until_complete(connections.close_all())
finally:
if not loop.is_closed():
loop.close()