diff --git a/meshirc.py b/meshirc.py
@@ -4,7 +4,6 @@
import argparse
import asyncio
import logging
-import logging.handlers
import ssl
import time
@@ -33,6 +32,10 @@ grey = '14'
light_grey = '15'
+# Logging Configuration
+logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(funcName)s - %(message)s')
+
+
def color(msg: str, foreground: str, background: str = None) -> str:
'''
Color a string with the specified foreground and background colors.
@@ -45,29 +48,9 @@ def color(msg: str, foreground: str, background: str = None) -> str:
return f'\x03{foreground},{background}{msg}{reset}' if background else f'\x03{foreground}{msg}{reset}'
-def ssl_ctx(verify: bool = False, cert_path: str = None, cert_pass: str = None) -> ssl.SSLContext:
- '''
- Create a SSL context for the connection.
-
- :param verify: Verify the SSL certificate.
- :param cert_path: The path to the SSL certificate.
- :param cert_pass: The password for the SSL certificate.
- '''
-
- ctx = ssl.create_default_context() if verify else ssl._create_unverified_context()
-
- if cert_path:
- ctx.load_cert_chain(cert_path) if not cert_pass else ctx.load_cert_chain(cert_path, cert_pass)
-
- return ctx
-
-
class Bot():
def __init__(self):
self.nickname = 'MESHTASTIC'
- self.username = 'MESHT' # MESHT@STIC
- self.realname = 'git.acid.vegas/meshtastic'
- self.connected = False
self.reader = None
self.writer = None
self.last = time.time()
@@ -114,17 +97,14 @@ class Bot():
'host' : args.server,
'port' : args.port,
'limit' : 1024,
- 'ssl' : ssl_ctx() if args.ssl else None,
- 'family' : 10 if args.v6 else 2,
- 'local_addr' : args.vhost if args.vhost else None # Can we just leave this as args.vhost?
+ 'ssl' : ssl._create_unverified_context() if args.ssl else None, # TODO: Do not use the args variable here
+ 'family' : 2, # AF_INET = 2, AF_INET6 = 10
+ 'local_addr' : None
}
self.reader, self.writer = await asyncio.wait_for(asyncio.open_connection(**options), 15)
- if args.password:
- await self.raw('PASS ' + args.password)
-
- await self.raw(f'USER {self.username} 0 * :{self.realname}')
+ await self.raw(f'USER MESHT 0 * :git.acid.vegas/meshtastic') # Static for now
await self.raw('NICK ' + self.nickname)
while not self.reader.at_eof():
@@ -146,20 +126,13 @@ class Bot():
'''
parts = data.split()
+
ident = parts[0][1:]
nick = parts[0].split('!')[0][1:]
target = parts[2]
msg = ' '.join(parts[3:])[1:]
- if target == self.nickname:
- if ident == 'acidvegas!stillfree@big.dick.acid.vegas':
- if msg.startswith('!raw') and len(msg.split()) > 1:
- option = ' '.join(msg.split()[1:])
- await self.raw(option)
- else:
- await self.sendmsg(nick, 'Do NOT message me!')
-
- if target.startswith('#'):
+ if target == args.channel: # TODO: Don't use the args variable here
if msg.startswith('!'):
if time.time() - self.last < 3:
if not self.slow:
@@ -167,33 +140,16 @@ class Bot():
await self.sendmsg(target, color('Slow down nerd!', red))
else:
self.slow = False
- if msg == '!help':
- await self.action(target, 'explodes')
- elif msg == '!ping':
- await self.sendmsg(target, 'Pong!')
- elif msg.startswith('!mesh') and len(msg.split()) > 1:
- message = ' '.join(msg.split()[1:])
- # Implement outgoing meshtastic message here
- #await self.sendmesh(message)
+ parts = msg.split()
+ if parts[0] == '!meshage' and len(parts) > 1:
+ message = ' '.join(parts[1:])
+ if len(message) > 255:
+ await self.sendmsg(target, color('Message exceeds 255 bytes nerd!', red))
+ # TODO: Send a meshtastic message (We have to ensure our outbounds from IRC don't loop back into IRC)
+
self.last = time.time() # Update the last command time if it starts with ! character to prevent command flooding
- async def eventConnect(self):
- '''Callback function for radio connection established.'''
-
- self.connected = True
-
- await self.action(args.channel, 'connected to the radio!')
-
-
- async def eventDisconnect():
- '''Callback function for radio connection lost.'''
-
- self.connected = False
-
- await self.action(args.channel, 'disconnected from the radio!')
-
-
async def handle(self, data: str):
'''
Handle the data received from the IRC server.
@@ -206,16 +162,13 @@ class Bot():
try:
parts = data.split()
- if data.startswith('ERROR :Closing Link:'):
- raise Exception('BANNED')
-
if parts[0] == 'PING':
await self.raw('PONG ' + parts[1])
elif parts[1] == '001': # RPL_WELCOME
await self.raw(f'MODE {self.nickname} +B')
await self.sendmsg('NickServ', f'IDENTIFY {self.nickname} simps0nsfan420')
- await asyncio.sleep(10)
+ await asyncio.sleep(10) # Wait for NickServ to identify or any channel join delays
await self.raw(f'JOIN {args.channel} {args.key if args.key else ""}')
elif parts[1] == '433': # ERR_NICKNAMEINUSE
@@ -245,36 +198,14 @@ class Bot():
logging.exception(f'Unknown error has occured! ({ex})')
-def setup_logger(log_filename: str, to_file: bool = False):
- '''
- Set up logging to console & optionally to file.
-
- :param log_filename: The filename of the log file
- :param to_file: Whether or not to log to a file
- '''
-
- sh = logging.StreamHandler()
- sh.setFormatter(logging.Formatter('%(asctime)s | %(levelname)9s | %(message)s', '%I:%M %p'))
- if to_file:
- fh = logging.handlers.RotatingFileHandler(log_filename+'.log', maxBytes=250000, backupCount=3, encoding='utf-8') # Max size of 250KB, 3 backups
- fh.setFormatter(logging.Formatter('%(asctime)s | %(levelname)9s | %(filename)s.%(funcName)s.%(lineno)d | %(message)s', '%Y-%m-%d %I:%M %p')) # We can be more verbose in the log file
- logging.basicConfig(level=logging.NOTSET, handlers=(sh,fh))
- else:
- logging.basicConfig(level=logging.NOTSET, handlers=(sh,))
-
-
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Connect to an IRC server.')
parser.add_argument('server', help='The IRC server address.')
parser.add_argument('channel', help='The IRC channel to join.')
- parser.add_argument('--password', help='The password for the IRC server.')
parser.add_argument('--port', type=int, help='The port number for the IRC server.')
parser.add_argument('--ssl', action='store_true', help='Use SSL for the connection.')
- parser.add_argument('--v4', action='store_true', help='Use IPv4 for the connection.')
- parser.add_argument('--v6', action='store_true', help='Use IPv6 for the connection.')
parser.add_argument('--key', default='', help='The key (password) for the IRC channel, if required.')
- parser.add_argument('--vhost', help='The VHOST to use for connection.')
args = parser.parse_args()
if not args.channel.startswith('#'):
@@ -282,11 +213,11 @@ if __name__ == '__main__':
if not args.port:
args.port = 6697 if args.ssl else 6667
+ elif args.port < 1 or args.port > 65535:
+ raise ValueError('Port must be between 1 and 65535.')
print(f'Connecting to {args.server}:{args.port} (SSL: {args.ssl}) and joining {args.channel} (Key: {args.key or 'None'})')
- setup_logger('skeleton', to_file=True)
-
bot = Bot()
asyncio.run(bot.connect())
\ No newline at end of file
diff --git a/meshtastic_serial.py b/meshtastic_serial.py
@@ -181,10 +181,14 @@ class MeshtasticClient(object):
'''
sender = packet['from']
+ to = packet['to']
msg = packet['decoded']['payload'].decode('utf-8')
id = self.nodes[sender]['user']['id'] if sender in self.nodes else '!unk '
name = self.nodes[sender]['user']['longName'] if sender in self.nodes else 'UNK'
- logging.info(f'{id} - {name}: {msg}')
+ target = self.nodes[to]['user']['longName'] if to in self.nodes else 'UNK'
+
+ logging.info(f'{id} {name} -> {target}: {msg}')
+ print(packet)
def event_user(self, packet: dict, interface):
@@ -258,3 +262,9 @@ if __name__ == '__main__':
finally:
logging.info('Connection to radio lost')
+'''
+Notes:
+ conf = self.interface.localNode.localConfig
+ ok = interface.getNode('^local')
+ print(ok.channels)
+'''
+\ No newline at end of file
| |