File size: 3,971 Bytes
9e6af01
 
 
 
 
 
 
 
 
 
3a12425
9e6af01
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5187cb9
 
 
 
 
 
3a12425
5187cb9
 
 
 
 
 
 
3a12425
5187cb9
3a12425
 
5187cb9
3a12425
 
5187cb9
 
3a12425
5187cb9
 
 
 
 
3a12425
5187cb9
 
 
 
 
 
 
 
 
 
 
 
3a12425
5187cb9
 
 
 
 
 
 
 
 
 
9e6af01
 
 
 
 
 
 
 
3a12425
 
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
from flask import Flask, render_template, Response
from sonatoki.ilo import Ilo
from sonatoki.Configs import PrefConfig
from atproto import FirehoseSubscribeReposClient, parse_subscribe_repos_message
from atproto import CAR, models
import json
import re
import emoji
import queue
import threading
from werkzeug.serving import run_simple

app = Flask(__name__)
message_queue = queue.Queue()

# Your existing code for Ilo and JSONExtra remains the same
ilo = Ilo(**PrefConfig)

class JSONExtra(json.JSONEncoder):
    def default(self, obj):
        try:
            return json.JSONEncoder.default(self, obj)
        except:
            return repr(obj)

def clean_text(text: str) -> str:
    text = emoji.replace_emoji(text, replace='')
    text = re.sub(r'https?://\S+', '', text)
    text = re.sub(r'[^A-Za-z\s]', '', text)
    text = text.strip()
    return text

def process_firehose():
    client = FirehoseSubscribeReposClient()

    def on_message_handler(message):
        commit = parse_subscribe_repos_message(message)
        if not isinstance(commit, models.ComAtprotoSyncSubscribeRepos.Commit):
            return
        car = CAR.from_bytes(commit.blocks)
        for op in commit.ops:
            if op.action in ["create"] and op.cid:
                raw = car.blocks.get(op.cid)
                cooked = models.get_or_create(raw, strict=False)
                if not cooked:
                    continue

                if cooked.py_type == "app.bsky.feed.post":
                    cleaned_text = clean_text(raw.get("text", ""))
                    if not cleaned_text or len(cleaned_text.split()) < 3:
                        continue

                    if not ilo.is_toki_pona(cleaned_text.lower()):
                        continue

                    url = f'https://bsky.app/profile/{commit.repo}/post/{op.path.split("/")[1]}'
                    message_queue.put({'text': raw.get("text", ""), 'url': url})

    client.start(on_message_handler)

def generate_sse():
    while True:
        message = message_queue.get()
        yield f"data: {json.dumps(message)}\n\n"

@app.route('/')
def index():
    return """<!DOCTYPE html>
<html>
<head>
    <title>Toki Pona Live Stream</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            max-width: 800px;
            margin: 0 auto;
            padding: 20px;
            background-color: #f5f5f5;
        }
        .message {
            background: white;
            padding: 15px;
            margin: 10px 0;
            border-radius: 5px;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
        }
        a {
            color: #0066cc;
            text-decoration: none;
        }
        h1 {
            text-align: center;
        }
    </style>
</head>
<body>
    <h1>Toki Pona Live Stream</h1>
    <div id="messages"></div>

    <script>
        const evtSource = new EventSource("/stream");
        const messages = document.getElementById('messages');
        
        evtSource.onmessage = function(event) {
            const data = JSON.parse(event.data);
            const messageDiv = document.createElement('div');
            messageDiv.className = 'message';
            messageDiv.innerHTML = `
                <p>${data.text}</p>
                <a href="${data.url}" target="_blank">View on Bluesky</a>
            `;
            messages.insertBefore(messageDiv, messages.firstChild);
            
            if (messages.children.length > 50) {
                messages.removeChild(messages.lastChild);
            }
        };
    </script>
</body>
</html>"""

@app.route('/stream')
def stream():
    return Response(generate_sse(), mimetype='text/event-stream')

if __name__ == '__main__':
    # Start the firehose processing in a separate thread
    threading.Thread(target=process_firehose, daemon=True).start()
    # Use run_simple instead of app.run
    run_simple('localhost', 7860, app, use_reloader=True, use_debugger=True)