#!/usr/bin/env python3 import os import sys import time from datetime import datetime import subprocess as sp from subprocess import call import tempfile LIST_MESSAGES_LIMIT = "30" chan_dir = ".config/gab/channels" ## not used yet. todo: move all gab channel logs to this dir current_user = os.environ.get('USER') log = {} help_text = """ GAB - A simple chat interface syntax: gab [flag] [value] flag value -------------------- --------------- -h, --help, help -m, --msg, msg Quoted text with the msg being added to chat -l, --list, list An integer representing the number of rows you'd like to view, default 5 -b, --block, block A username to block/ignore -u, --unblock, unblock A username to unblock/unignore -c, --channel, channel Name of the channel you would like to join or create. An empty value lists the available channels. -n, --new, new Check for new, unread messages. -N, --new-verbose, new-verbose Check for new, unread messages, creating output even when no new messages exist. Channels names ending in ! are private. They can be joined by anyone that knows they exist, but will not be listed in the channel listings. To return to the main/default channel, simply switch channels to 'gab': gab channel gab Examples: gab msg "Good afternoon!" gab list 20 gab block annoyingperson gab channel my-secret-channel! gab channel a-public-channel gab unblock somedude """ title = "\033[1mGAB v2.2\033[0m" def get_chan(): fp = "/home/{}/.gab_chan".format(current_user) if not os.path.isfile(fp): return "" with open(fp, 'r') as chanfile: channel = chanfile.read().split("\n")[0].strip() if channel: channel = "-" + channel return channel def blocked_users(): fp = "/home/{}/.gab_block".format(current_user) if not os.path.isfile(fp): return [] with open(fp, 'r') as blockfile: return blockfile.read().split("\n") def get_files(channel): return [[x, "/home/{}/.gab{}".format(x, channel)] for x in os.listdir("/home/") if x not in blocked_users() and os.access("/home/"+x, os.R_OK)] def get_user_channels(user_dir): chans = {"gab"} for y in os.listdir(user_dir): if len(y) > 5 and y[:5] == ".gab-" and y[-1] != "!": chans.add(y[5:]) return chans def get_chan_list(): chans = {"gab"} for x in os.listdir("/home/"): if not os.access("/home/" + x, os.R_OK): continue for y in os.listdir("/home/" + x): if len(y) > 5 and y[:5] == ".gab-" and y[-1] != "!": chans.add(y[5:]) return chans def list_channels(): chans = get_chan_list() chan = get_chan() print(title) print("\n\033[1mAvailable channels\033[0m:") for x in chans: if chan[1:] == x: x = x + " *" elif chan == "" and x == "gab": x = x + " *" print(x) def read_file(user, path, num_lines): global log if os.path.isfile(path) and os.access(path, os.R_OK): if "/home/" + current_user + "/" in path: os.utime(path, None) ## update file's timestamp on each read with open(path, "r") as f: for line in range(num_lines): msg = f.readline().split("|", 1) dt = msg[0].strip() if len(msg) == 2 and float(dt) < time.time(): msg = msg[1].strip() if msg[:3] == "/me": log[dt] = "\033[7m * * * \033[0m \033[3m{}\033[0m".format(msg.replace("/me", user)) else: log[dt] = "\033[7m {} \033[0m {}\033[0m".format(user, msg) def list_messages(count=LIST_MESSAGES_LIMIT): global log chan = get_chan() files = get_files(chan) try: count = int(count) except ValueError: print("Error parsing list number, using default: 5") count = 5 for x in files: read_file(x[0], x[1], count) sorted_keys = list(log.keys()) if len(sorted_keys) > 0: sorted_keys.sort(reverse=True) last_time = float(sorted_keys[0]) sorted_keys = sorted_keys[:min(len(sorted_keys), count)] sorted_keys.sort(reverse=False) print(title) print("Current channel: \033[1m{}\033[0m".format(chan[1:] if chan else "gab")) print("Last message: {}".format(diff_time_from_now(last_time))) print("\n- - -") for key in range(min(count, len(sorted_keys))): print(log[sorted_keys[key]]) print("- - -") else: print(title) print("Current channel: \033[1m{}\033[0m".format(chan[1:] if chan else "gab")) print() print("There are no messages to display") def diff_time_from_now(t, n=time.time()): diff = n - t diff = int(diff) out = "" # Days if diff // (3600 * 24) > 0: days = diff // (3600 * 24) out += "{}d ".format(days) diff = diff - (days * 3600 * 24) # Hours if diff // 3600 > 0: out += "{}h ".format(diff // 3600) diff = diff - (diff // 3600 * 3600) # Minutes if diff // 60 > 0: out += "{}m ".format(diff // 60) diff = diff - (diff // 60 * 60) # Seconds out += "{}s ago".format(diff) return out def add_message(msg): timestamp = str(time.time()) output = "{}|{}\n".format(timestamp, msg.strip()) fp = "/home/{}/.gab{}".format(current_user, get_chan()) try: with open(fp, 'r') as original: data = original.read(12000) with open(fp, 'w') as modified: modified.write(output) modified.write(data) except FileNotFoundError: with open(fp, 'a') as modified: modified.write(output) except: print("Error adding text to chatlog") return print("Successfully added text to chatlog") try: os.chmod(fp, 0o644) except PermissionError: print("You do not have permission to modify the chatlog") except FileNotFoundError: print("The chatlog does not exist") def block_user(user_to_block): fp = "/home/{}/.gab_block".format(current_user) block_fp = "/home/{}/".format(user_to_block) if not os.path.isdir(block_fp): print("User '{}' does not exist, no action taken".format(user_to_block)) return with open(fp, 'a') as blockfile: blockfile.write(user_to_block) blockfile.write("\n") print("User '{}' has been blocked, you will not see their messages".format(user_to_block)) return def unblock_user(user_to_unblock): fp = "/home/{}/.gab_block".format(current_user) unblock_fp = "/home/{}/".format(user_to_unblock) if not os.path.isfile(fp): print("You do not have any users on your block list, no action taken") return if not os.path.isdir(unblock_fp): print("User '{}' does not exist, no action taken".format(user_to_unblock)) return blocked_users_list = [x for x in blocked_users() if x != user_to_unblock] with open(fp, 'w') as blocked: blocked.write("\n".join(blocked_users_list)) print("User '{}' has been removed from your block list".format(user_to_unblock)) def switch_channel(newchan): chan = newchan if newchan == "gab": newchan = "" fp = "/home/{}/.gab_chan".format(current_user) with open(fp, 'w') as chanfile: chanfile.write(newchan) print("You are now viewing the '{}' channel.".format(chan)) list_messages() def new_scan(silent=True): unread_chans = [] chans = get_user_channels("/home/" + current_user) for c in chans: my_timestamp = chan_timestamp = 0.0 if c == "gab": files = get_files("") else: files = get_files("-" + c) for f in files: if os.path.isfile(f[1]): try: with open(f[1]) as fh: timestamp = float(fh.readline().split("|")[0]) if timestamp > float(chan_timestamp): chan_timestamp = timestamp if f[0] == current_user: atime= time.ctime(os.stat(f[1]).st_atime) my_timestamp = time.mktime(datetime.strptime(atime, "%a %b %d %H:%M:%S %Y").timetuple()) except: continue if my_timestamp - chan_timestamp < 0: unread_chans.append(c) if len(unread_chans): print("New gab messages in: " + ", ".join(unread_chans)) else: if silent == False: print("gab: no unread messages.") exit(1) def parse_command(): args = sys.argv[1:] if not len(args): list_messages() elif args[0] in ["-h", "--help", "help"]: print(help_text) elif args[0] in ["list", "--list", "-l"]: if len(args) == 2: list_messages(args[1]) else: print("Invalid command input to 'list'") elif args[0] in ["new", "--new", "-n"]: new_scan() elif args[0] in ["new-verbose", "--new-verbose", "-N"]: new_scan(silent=False) elif args[0] in ["-m", "--msg", "msg"]: if len(args) < 2: # code from https://stackoverflow.com/a/6309753 EDITOR = os.environ.get('EDITOR') if os.environ.get('EDITOR') else 'vi' with tempfile.NamedTemporaryFile(suffix=".tmp") as tf: tf.flush() call([EDITOR, tf.name]) tf.seek(0) edited_message = tf.read() lines = edited_message.decode("utf-8").split("\n") for line in lines: if len(line.split()): add_message(line) elif len(args) > 2: print("Expected a message, but received too many arguments. Did you put your message in quotes?") else: add_message(args[1]) elif args[0] in ["-b", "--block", "block"]: if len(args) < 2: print("Expected a user to block, but one was not received") elif len(args) > 2: print("Expected a user to block, but received too many arguments") else: block_user(args[1]) elif args[0] in ["-u", "--unblock", "unblock"]: if len(args) < 2: print("Expected a user to unblock, but one was not received") elif len(args) > 2: print("Expected a user to unblock, but received too many arguments") else: unblock_user(args[1]) elif args[0] in ["-c", "--channel", "channel"]: if len(args) < 2: # print("Returning to default channel.") # switch_channel("gab") list_channels() elif len(args) > 2: print("Expected a channel to join, but received too many arguments") else: switch_channel(args[1]) else: print("Unknown command: {}".format(args[0])) if __name__ == '__main__': parse_command()