home bbs files messages ]

Forums before death by AOL, social media and spammers... "We can't have nice things"

   alt.magick      Meh.. another magic/spellcasting forum      90,437 messages   

[   << oldest   |   < older   |   list   |   newer >   |   newest >>   ]

   Message 90,425 of 90,437   
   street@shellcrash.com to All   
   AI Generated Usenet Client Update (1/3)   
   24 Dec 25 01:36:43   
   
   AI Generated Usenet Client in Python   
      
   If you have any improvements to this or helpful ideas I would like to see them.   
      
   https://github.com/alt-magick/Newsgroup-Client-/   
      
   Newsgroup-Client   
      
   For Termux   
      
   This is a Python script that runs on Android in Termux. It is a full newsgroup   
   client for Usenet.   
      
   Menu Functions   
      
   Enter = Read - Opens current article   
      
   Space = Next - Moves to next article   
      
   Backspace = Previous - Moves to previous article   
      
   L = Reload - Loads new posts   
      
   J = Jump - Skip to an article   
      
   G = Group - Changes newsgroup   
      
   B = Batch - List articles   
      
   F = Author - Finds posts by author   
      
   S = Subject - Finds posts by subject   
      
   M = Body - Searches through message bodies   
      
   R = Replies - Reads replies to articles   
      
   N = New post - Posts to newsgroup   
      
   Y = Reply - Replies to current article   
      
   P = Page - Sets the length of each page   
      
   C = Reconnect - Reconnects to server   
      
   Q = Quit - Closes the newsgroup client   
      
   Hint: Pinch the terminal window to change the font size on Termux.   
      
   Full Python Code:   
      
   #!/usr/bin/env python3   
   import nntplib   
   import sys   
   import termios   
   import tty   
   import re   
   import quopri   
   import base64   
   from email.message import EmailMessage   
   import os   
   import uuid   
      
   # ================= USER CONFIG =================   
   NNTP_SERVER = "usnews.blocknews.net"   
   NNTP_PORT   = 563   
   USERNAME    = ""   
   PASSWORD    = ""   
   PAGE_LINES             = 25   
   MAX_ARTICLES_LIST      = 200   
   START_GROUP            = "alt.magick"   
   SHOW_REPLY_COUNT_MAIN  = True   
   # ==============================================   
      
   RE_REPLY = re.compile(r"^(re|fwd):", re.IGNORECASE)   
   CLEAN_RE = re.compile(r"[ ---]")   
      
   # ---------- STATUS ----------   
   STATUS_LINE = ""   
   def set_status(msg):   
       global STATUS_LINE   
       STATUS_LINE = msg   
      
   def show_status():   
       global STATUS_LINE   
       if STATUS_LINE:   
           print(f"   
   [{STATUS_LINE}]   
   ")   
           STATUS_LINE = ""   
      
   # ---------- RAW KEY INPUT ----------   
   def get_key():   
       fd = sys.stdin.fileno()   
       old = termios.tcgetattr(fd)   
       try:   
           tty.setraw(fd)   
           return sys.stdin.read(1)   
       finally:   
           termios.tcsetattr(fd, termios.TCSADRAIN, old)   
      
   def prompt(text):   
       print()   
       sys.stdout.write(text)   
       sys.stdout.flush()   
       return sys.stdin.readline().strip()   
      
   # ---------- PAGER ----------   
   def paged_print(lines):   
       i = 0   
       while i < len(lines):   
           for line in lines[i:i + PAGE_LINES]:   
               print(line)   
           i += PAGE_LINES   
           if i >= len(lines):   
               break   
           print("   
   --- ENTER = next page | SPACE = skip ---   
   ")   
           if get_key() == " ":   
               break   
       print()   
      
   # ---------- BODY DECODER ----------   
   def decode_body_line(line_bytes):   
       s = line_bytes.decode("utf-8", errors="replace")   
       s = CLEAN_RE.sub("", s)   
       if "=" in s:   
           try:   
               s = quopri.decodestring(s).decode("utf-8", errors="replace")   
           except Exception:   
               pass   
       if re.fullmatch(r"[A-Za-z0-9+/=\s]+", s) and len(s.strip()) > 20:   
           try:   
               s = base64.b64decode(s, validate=True).decode("utf-8",   
   errors="replace")   
           except Exception:   
               pass   
       return s   
      
   # ---------- HEADER SANITIZER ----------   
   def sanitize_header(s):   
       return "".join(c if 32 <= ord(c) <= 126 else " " for c in s).strip()   
      
   # ---------- DISPLAY ----------   
   def show_article(nntp, num, group=None):   
       try:   
           _, body = nntp.body(str(num))   
           print()   
           if group:   
               print(f"Group: {group}   
   ")   
           paged_print([decode_body_line(l) for l in body.lines])   
           print()   
       except Exception as e:   
           set_status(f"Fetch failed: {e}")   
      
   # ---------- GROUP LOAD ----------   
   def reload_group(nntp, group):   
       try:   
           _, _, first, last, _ = nntp.group(group)   
           first, last = int(first), int(last)   
           _, overviews = nntp.over((max(first, last - MAX_ARTICLES_LIST), last))   
           posts = []   
           rel = 1   
           for num, hdr in reversed(overviews):   
               subject = hdr.get("subject", "")   
               if RE_REPLY.match(subject):   
                   continue   
               msgid = hdr.get("message-id", "")   
               replies = sum(1 for _, h in overviews if msgid in h.   
   et("references", "")) if SHOW_REPLY_COUNT_MAIN else 0   
               posts.append({   
                   "rel_num": rel,   
                   "num": int(num),   
                   "from": CLEAN_RE.sub("", hdr.get("from", "?")),   
                   "date": hdr.get("date", "?"),   
                   "subject": CLEAN_RE.sub("", subject),   
                   "replies": replies,   
                   "msgid": msgid   
               })   
               rel += 1   
           return posts   
       except Exception as e:   
           set_status(f"Reload failed: {e}")   
           return []   
      
   # ---------- HEADER SEARCH ----------   
   def header_search(nntp, group, field, keyword, count):   
       _, _, first, last, _ = nntp.group(group)   
       start = max(int(first), int(last) - count + 1)   
       _, overviews = nntp.over((start, last))   
       results = []   
       rel = 1   
       total = len(overviews)   
       for idx, (num, hdr) in enumerate(reversed(overviews), 1):   
           sys.stdout.write(f"   
   Searching headers... ({idx}/{total})")   
           sys.stdout.flush()   
           value = CLEAN_RE.sub("", hdr.get(field, ""))   
           if keyword.lower() in value.lower():   
               msgid = hdr.get("message-id", "")   
               replies = sum(1 for _, h in overviews if msgid in h.   
   et("references", "")) if SHOW_REPLY_COUNT_MAIN else 0   
               results.append({   
                   "rel_num": rel,   
                   "num": int(num),   
                   "from": CLEAN_RE.sub("", hdr.get("from", "?")),   
                   "date": hdr.get("date", "?"),   
                   "subject": CLEAN_RE.sub("", hdr.get("subject", "")),   
                   "replies": replies,   
                   "msgid": msgid   
               })   
           rel += 1   
       sys.stdout.write("   
   Search complete!              
   ")   
       return results   
      
   # ---------- BODY SEARCH ----------   
   def body_search(nntp, group, keyword, count):   
       _, _, first, last, _ = nntp.group(group)   
       start = max(int(first), int(last) - count + 1)   
       _, overviews = nntp.over((start, last))   
       matches = []   
       rel = 1   
       total = len(overviews)   
       for idx, (num, hdr) in enumerate(reversed(overviews), 1):   
           sys.stdout.write(f"   
   Searching bodies... ({idx}/{total})")   
           sys.stdout.flush()   
           try:   
               _, body = nntp.body(str(num))   
               text = "   
   ".join(decode_body_line(l) for l in body.lines)   
               if keyword.lower() in text.lower():   
                   msgid = hdr.get("message-id", "")   
                   replies = sum(1 for _, h in overviews if msgid in   
   h.get("references", "")) if SHOW_REPLY_COUNT_MAIN else 0   
                   matches.append({   
                       "rel_num": rel,   
                       "num": int(num),   
                       "from": CLEAN_RE.sub("", hdr.get("from", "?")),   
                       "date": hdr.get("date", "?"),   
                       "subject": CLEAN_RE.sub("", hdr.get("subject", "")),   
      
   [continued in next message]   
      
   --- SoupGate-Win32 v1.05   
    * Origin: you cannot sedate... all the things you hate (1:229/2)   

[   << oldest   |   < older   |   list   |   newer >   |   newest >>   ]


(c) 1994,  bbs@darkrealms.ca