forked from leobeosab/AwsEc2DiscordBot
-
Notifications
You must be signed in to change notification settings - Fork 1
/
ecsbot.py
154 lines (139 loc) · 6.13 KB
/
ecsbot.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
import discord, asyncio, os, boto3, socket, traceback
client = discord.Client()
ecs = boto3.client('ecs')
START='start'
STOP='stop'
STATE='state'
BOUNCE='bounce'
INFO='info'
COMMANDS=[START,STOP,STATE,BOUNCE,INFO]
COMMANDS_STR=', '.join(COMMANDS)
def get_cluster():
return next(item for item in ecs.list_clusters()['clusterArns'] if 'minebot' in item)
def get_service(guild, cluster=get_cluster()):
# find first cluster name containing 'minebot' (should only be one)
services = ecs.describe_services(cluster=cluster,
services=ecs.list_services(cluster=cluster)['serviceArns'],
include=['TAGS'])['services']
if (len(services) == 0):
return None
else:
return next((item for item in services if (is_for_guild(item, guild))), None)
def is_for_guild(service, guild):
tag = next((item for item in service['tags'] if item['key'] == 'guild'), None)
return (tag != None and tag['value'] == guild)
def start_service(service):
ecs.update_service(cluster=service['clusterArn'],
service=service['serviceArn'],
desiredCount=1)
def stop_service(service):
ecs.update_service(cluster=service['clusterArn'],
service=service['serviceArn'],
desiredCount=0)
def get_tasks(service):
tasks = ecs.list_tasks(cluster=service['clusterArn'], serviceName=service['serviceName'])['taskArns']
if tasks == None or len(tasks) == 0:
return []
else:
return ecs.describe_tasks(cluster=service['clusterArn'], tasks=tasks)['tasks']
def task_ip(task):
eni = next((item for item in task['attachments'] if item['type'] == 'ElasticNetworkInterface'), None)
if (eni == None):
return None
eni_id = next((item for item in eni['details'] if item['name'] == 'networkInterfaceId'), None)
if (eni_id == None):
return None
intf = boto3.resource('ec2').NetworkInterface(eni_id['value'])
if (intf == None or intf.association_attribute == None):
return None
else:
return intf.association_attribute['PublicIp']
def print_status(service):
if (service == None):
print("Cannot find service")
else:
print(f'Found service with {service["runningCount"]} running tasks and {service["desiredCount"]} desired tasks')
tasks = get_tasks(service)
if (len(tasks) > 0):
print(f'Service has {len(tasks)} task, first task accessible at {task_ip(tasks[0])}')
async def wait_until_stable(guild, cluster=get_cluster()):
state = get_service(guild, cluster)
while state['runningCount'] != state['desiredCount']:
await asyncio.sleep(5)
state = get_service(guild, cluster)
return
def current_state(service):
if (service == None):
return 'Cannot find service'
else:
if service['runningCount'] == 1 and service['desiredCount'] == 1:
return 'Minecraft is running and ready at ' + task_ip(get_tasks(service)[0])
elif service['runningCount'] == 1 and service['desiredCount'] == 0:
return 'Minecraft is stopping ...'
elif service['runningCount'] == 0 and service['desiredCount'] == 1:
return 'Minecraft is starting ...'
elif service['runningCount'] == 0 and service['desiredCount'] == 0:
return 'Minecraft is stopped'
else:
return 'FUBAR'
@client.event
async def on_ready():
print('Logged into discord as')
print(client.user.name)
print(client.user.id)
print('------------')
@client.event
async def on_message(message):
memberIDs = (member.id for member in message.mentions)
guild = str(message.channel.guild.id)
service = get_service(guild)
if service == None:
print(f'Unrecognised guild {guild} sent command "{message.content}" from user {client.user.id}')
return
elif (client.user.id in memberIDs):
print(f'Processing message "{message.content}" from user {client.user.id} and guild {guild}')
if STOP in message.content:
try:
stop_service(service)
await message.channel.send('Minecraft stopping ...')
await wait_until_stable(guild)
await message.channel.send(current_state(get_service(guild)))
except Exception as e:
print(traceback.format_exc())
await message.channel.send('Error stopping minecraft: ' + str(e))
elif START in message.content:
try:
start_service(service)
await message.channel.send('Minecraft starting ...')
await wait_until_stable(guild)
await message.channel.send(current_state(get_service(guild)))
except Exception as e:
print(traceback.format_exc())
await message.channel.send('Error starting minecraft: ' + str(e))
elif STATE in message.content:
try:
await message.channel.send(current_state(service))
except Exception as e:
print(traceback.format_exc())
await message.channel.send('Error getting status: ' + str(e))
elif BOUNCE in message.content:
try:
stop_service(service)
await message.channel.send('Minecraft stopping ...')
await wait_until_stable(guild)
start_service(service)
await message.channel.send('Minecraft starting again...')
await wait_until_stable(guild)
await message.channel.send(current_state(get_service(guild)))
except Exception as e:
await message.channel.send('Error restarting minecraft:' + str(e))
elif INFO in message.content:
await message.channel.send(f'Minecraft server start/stop bot. Commands are {COMMANDS_STR}')
await message.channel.send(current_state(service))
else:
await message.channel.send(f'Unrecognised command. Commands are {COMMANDS_STR}')
return
else:
# do nothing, message not for me
return
client.run(os.environ['AWSDISCORDTOKEN'])