comparison termbanator.py @ 0:f570713a7d31 default tip

start termbanator
author drewp@bigasterisk.com
date Sun, 25 Jun 2023 20:01:38 -0700
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:f570713a7d31
1 import datetime
2 import os
3 import pdb
4 import subprocess
5 import sys
6 import time
7 import logging
8 import traceback
9 from typing import Any, Optional, Tuple
10
11 from javascript import On, Once, console, require
12
13 logging.basicConfig(
14 level=logging.INFO,
15 format="%(asctime)s [%(levelname)s] [line %(lineno)s] %(message)s")
16 log = logging.getLogger()
17
18 mineflayer = require("mineflayer", "latest")
19 Vec3 = require("vec3").Vec3
20 registry = require('prismarine-registry')('1.19.3')
21 Block = require("prismarine-block")(registry)
22
23 host, port, rconPort = ('bang', 25665, 25675)
24
25 mfbot = mineflayer.createBot({
26 "host": host,
27 "port": port,
28 "username": 'termbanator',
29 'auth': 'offline',
30 'version': '1.19.3',
31 })
32 log.info('created')
33
34
35 class Player:
36
37 def __init__(self, player):
38 self._p = player
39 if self._p is None:
40 raise TypeError(f'player was {player}')
41 self.username = self._p.username
42
43 def __repr__(self):
44 return f'Player({self.username})'
45
46 def position(self) -> Tuple[float, float, float]:
47 if self._p.entity is None:
48 return getDistantPlayerPos(self.username)
49 pos = self._p.entity.position
50 return (round(pos.x, 2), round(pos.y, 2), round(pos.z, 2))
51
52
53 def getDistantPlayerPos(username) -> Tuple[float, float, float]:
54 out = subprocess.check_output([
55 "/home/drewp/Downloads/mcrcon/mcrcon", "-H", host, "-P",
56 str(rconPort), "-p", "111", f"/data get entity @p[name={username}] Pos"
57 ],
58 encoding='utf8')
59 words = out.split("the following entity data: ")[1].split('[')[1].split(
60 ']')[0].split(',')
61 return tuple(round(float(v.rstrip('d')), 2) for v in words)
62
63
64 class Bot:
65
66 def __init__(self, mineflayer_bot: Any):
67 self._bot = mineflayer_bot
68 self.me = Player(self._bot)
69 mcData = require('minecraft-data')(self._bot.version)
70 self.pf_module = require('mineflayer-pathfinder')
71 self._bot.loadPlugin(self.pf_module.pathfinder)
72
73 self.movements = self.pf_module.Movements(self._bot, mcData)
74 self.movements.allow1by1towers = True
75 self.movements.canDig = False
76 self.movements.scafoldingBlocks.push(
77 self._bot.registry.itemsByName['dirt'].id)
78 self.movements.maxDropDown = 300
79 self.movements.allowFreeMotion = True
80 self.movements.canOpenDoors = True
81
82 self._bot.removeAllListeners('chat')
83
84 self.walkStartTime = 0
85 self.waitUntil = time.time() + 2
86 self.target: Optional[Player] = None
87 self.announcedToTarget = False
88
89 def chat(self, txt):
90 log.info(f'say {txt!r}')
91 self._bot.chat(txt)
92
93 def teleport(self, x, y, z):
94 self._bot.chat(f'/tp {x} {y} {z}')
95
96 def setPathfinderGoal(self, x, y, z, rng):
97 pos = self.me.position()
98 log.info(f'now at {pos}, pathfinding to {x} {y} {z} {rng=}')
99 goal = self.pf_module.goals.GoalNear(x, y, z, rng)
100 self._bot.pathfinder.setMovements(self.movements)
101 self._bot.pathfinder.setGoal(goal)
102 self.walkStartTime = time.time()
103
104 def isMoving(self) -> bool:
105 return self._bot.pathfinder.isMoving()
106
107 def stopPathfinder(self):
108 log.info("Stopping pathfinder")
109 self._bot.pathfinder.stop()
110
111 def ban(self, player: Player, reason: str = "good night"):
112 self.chat(f'/ban {player.username} {reason}')
113
114 def quit(self):
115 self._bot.quit('done')
116 os.kill(os.getpid(), 15)
117
118 ########## below here doesn't use self._bot ##########
119
120 def onLogin(self):
121 self.chat("Beware the termbanator")
122 # self.teleport(-160, 200, 280)
123
124 def walkTo(self, player: Player):
125 self.target = player
126 self.announcedToTarget = False
127 log.info(f'walk to {self.target}')
128 pos = player.position()
129 self.setPathfinderGoal(pos[0], pos[1], pos[2], rng=3)
130
131 def stillWalking(self, walkMinTime=3):
132 if self.walkStartTime and time.time(
133 ) < self.walkStartTime + walkMinTime:
134 return True
135 return self.isMoving()
136
137 def update(self, waitBetweenTargets=9):
138 try:
139 now = time.time()
140
141 if self.waitUntil > 0 and now < self.waitUntil:
142 log.debug(f'waiting {round(self.waitUntil-now, 1)} more sec')
143 return
144 self.waitUntil = 0
145
146 if self.stillWalking():
147 pos = self.me.position()
148 log.info(f'still moving to {self.target}: {pos=}')
149 return
150
151 self.stopPathfinder()
152
153 if self.target:
154 if not self.announcedToTarget:
155 minsLeft = round(
156 (self.getTodayEndTime() - time.time()) / 60, 1)
157
158 if minsLeft > 0:
159 mins = 'minutes' if minsLeft != 1 else 'minute'
160 self.chat(
161 f'hi {self.target.username} - {minsLeft} {mins} left'
162 )
163 else:
164 self.ban(self.target)
165
166 self.announcedToTarget = True
167 self.sleepFor(waitBetweenTargets)
168 return
169
170 log.info('follow next player')
171
172 next = players.after(self.target)
173 try:
174 self.walkTo(next)
175 except ValueError as e:
176 log.warning(repr(e))
177 self.sleepFor(8)
178 except EveryoneGone:
179 bot.quit()
180 except Exception as e:
181 traceback.print_exc()
182 log.warning(repr(e))
183 self.sleepFor(60)
184
185 def getTodayEndTime(self) -> float:
186 current_date = datetime.datetime.now().date()
187 end_datetime = datetime.datetime.combine(current_date,
188 datetime.time(19, 0))
189
190 return end_datetime.timestamp()
191
192 def sleepFor(self, sec):
193 log.info(f'sleeping for {sec}')
194 self.waitUntil = time.time() + sec
195
196
197 class EveryoneGone(ValueError):
198 pass
199
200
201 class Players:
202
203 def __init__(self, mineflayer_bot: Any):
204 self._b = mineflayer_bot
205
206 def byName(self, name) -> Player:
207 # only works for 'nearby' players- useless
208 return Player(self._b.players[name])
209
210 def after(self, player: Optional[Player]) -> Player:
211 otherPlayers = [p for p in self._b.players if p != 'termbanator']
212 log.info(f'{otherPlayers=}')
213 if not otherPlayers:
214 log.info('everyone is gone')
215 raise EveryoneGone()
216 # import pdb;pdb.set_trace()
217 prevPlayer = player.username if player else None
218
219 try:
220 i = otherPlayers.index(prevPlayer)
221 except (AttributeError, ValueError):
222 i = 0
223 i = (i + 1) % len(otherPlayers)
224
225 return self.byName(otherPlayers[i])
226
227
228 bot = Bot(mfbot)
229 players = Players(mfbot)
230
231
232 @On(mfbot, "login")
233 def login(this):
234 bot.onLogin()
235
236
237 @On(mfbot, 'chat')
238 def handleMsg(this, sender, message, *args):
239 if sender and (sender != 'termbanator'):
240 pass
241
242
243 @On(mfbot, 'time')
244 def onTime(this):
245 bot.update()
246
247
248 @On(mfbot, 'message')
249 def message(emitter, msg, *args):
250 log.info(f'got msg event {msg.toString()}')