RendiXD commited on
Commit
5467a14
Β·
verified Β·
1 Parent(s): 246bd28

Upload 3 files

Browse files
Files changed (3) hide show
  1. agicto.py +254 -0
  2. app.py +373 -0
  3. index.html +169 -0
agicto.py ADDED
@@ -0,0 +1,254 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import bs4
2
+ import time
3
+ import requests
4
+ import ipaddress
5
+
6
+
7
+ BASE_URL = "https://api.agicto.cn/v1"
8
+ API_KEYS = []
9
+ REVOKE_KEYS = []
10
+
11
+
12
+ class TempMail:
13
+ base_api = "https://www.1secmail.com/api/v1/"
14
+
15
+ def __init__(self):
16
+ self.mail: str = self.random_mail()
17
+
18
+ def __str__(self):
19
+ return self.mail
20
+
21
+ def random_mail(self):
22
+ return requests.get(
23
+ self.base_api, params={"action": "genRandomMailbox"}
24
+ ).json()[0]
25
+
26
+ def check_inbox(self):
27
+ mail, domain = self.mail.split("@")
28
+ mail = {"login": mail, "domain": domain}
29
+
30
+ inbox = requests.get(
31
+ self.base_api,
32
+ params={"action": "getMessages", **mail},
33
+ ).json()
34
+ if inbox:
35
+ inbox = requests.get(
36
+ self.base_api,
37
+ params={"action": "readMessage", "id": inbox[0]["id"], **mail},
38
+ )
39
+
40
+ return inbox.json()["body"]
41
+
42
+
43
+ def generate_api_key() -> str:
44
+ mail = TempMail()
45
+
46
+ sess = requests.Session()
47
+ sess.headers.update(
48
+ {
49
+ "user-agent": (
50
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
51
+ "AppleWebKit/537.36 (KHTML, like Gecko) "
52
+ "Chrome/89.0.142.86 Safari/537.36"
53
+ ),
54
+ "x-forwarded-for": str(
55
+ ipaddress.IPv4Address(random.randint(0, (1 << 32) - 1))
56
+ ),
57
+ }
58
+ )
59
+ sess.post(
60
+ BASE_URL + "/sendVerifyCode",
61
+ json={"email": str(mail), "channel": ""},
62
+ )
63
+
64
+ step = 0
65
+ while step < 20:
66
+ inbox = mail.check_inbox()
67
+ if inbox:
68
+ break
69
+ time.sleep(1)
70
+ step += 1
71
+
72
+ if not inbox:
73
+ raise Exception("Failed to get inbox")
74
+
75
+ soup = bs4.BeautifulSoup(inbox, "html.parser")
76
+ code = soup.find("div", {"class": "code-box"}).text
77
+
78
+ token = sess.post(
79
+ BASE_URL + "/loginByCode",
80
+ json={
81
+ "email": str(mail),
82
+ "verify_code": code,
83
+ "invite_code": "",
84
+ },
85
+ ).json()["data"]["access_token"]
86
+
87
+ api_key = sess.post(
88
+ BASE_URL + "/service/keyList",
89
+ headers={
90
+ "authorization": f"Bearer {token}",
91
+ },
92
+ ).json()["data"]["recordList"][0]["openKey"]
93
+ return api_key
94
+
95
+
96
+ import threading
97
+
98
+
99
+ def increase_api_keys(max=6, per_thread=3):
100
+ if len(API_KEYS) < max:
101
+ threads = [
102
+ threading.Thread(target=lambda: API_KEYS.append(generate_api_key()))
103
+ for _ in range(per_thread)
104
+ ]
105
+ [t.start() for t in threads]
106
+
107
+
108
+ import re
109
+ import random
110
+ import json
111
+
112
+ from werkzeug import exceptions
113
+
114
+
115
+ # non-stream
116
+ def chat_completion(**kwargs):
117
+ sess = requests.Session()
118
+ for i in range(max_retries := 3):
119
+ try:
120
+ api_key = random.choice(API_KEYS)
121
+ sess.headers.update(
122
+ {
123
+ "authorization": "Bearer " + api_key,
124
+ "x-forwarded-for": str(
125
+ ipaddress.IPv4Address(random.randint(0, (1 << 32) - 1))
126
+ ),
127
+ },
128
+ )
129
+ r = sess.post(BASE_URL + "/chat/completions", json=kwargs, timeout=30)
130
+ r.raise_for_status()
131
+
132
+ if r.json().get("error"):
133
+ raise requests.exceptions.HTTPError(response=r)
134
+
135
+ break
136
+ except IndexError:
137
+ continue
138
+ except requests.exceptions.Timeout:
139
+ continue
140
+ except requests.exceptions.HTTPError as e:
141
+ try:
142
+ data = e.response.json()
143
+ if data.get("error") or data.get("code") == 1:
144
+ msg = data.get("error", {}).get("message") or data.get("message")
145
+ if re.search(r"agicto", msg, re.IGNORECASE):
146
+ if api_key in API_KEYS:
147
+ API_KEYS.remove(api_key)
148
+ REVOKE_KEYS.append(api_key)
149
+ continue
150
+
151
+ raise exceptions.BadRequest(msg)
152
+ except json.JSONDecodeError:
153
+ pass
154
+
155
+ if i == max_retries - 1:
156
+ raise exceptions.ServiceUnavailable
157
+
158
+ try:
159
+ content: str = r.json()["choices"][0]["message"]["content"]
160
+ return bytes(content, "utf-8").decode("unicode_escape")
161
+ except:
162
+ raise exceptions.ServiceUnavailable
163
+
164
+
165
+ # stream
166
+ def chat_completion_stream(**kwargs):
167
+ kwargs["stream"] = True
168
+
169
+ sess = requests.Session()
170
+ for i in range(max_retries := 3):
171
+ try:
172
+ api_key = random.choice(API_KEYS)
173
+ sess.headers.update(
174
+ {
175
+ "authorization": "Bearer " + api_key,
176
+ "x-forwarded-for": str(
177
+ ipaddress.IPv4Address(random.randint(0, (1 << 32) - 1))
178
+ ),
179
+ },
180
+ )
181
+ r = sess.post(
182
+ BASE_URL + "/chat/completions", json=kwargs, stream=True, timeout=30
183
+ )
184
+ r.raise_for_status()
185
+ if "text/event-stream" not in r.headers.get("Content-Type"):
186
+ raise requests.exceptions.HTTPError(response=r)
187
+
188
+ break
189
+ except IndexError:
190
+ continue
191
+ except requests.exceptions.Timeout:
192
+ continue
193
+ except requests.exceptions.HTTPError as e:
194
+ try:
195
+ data = e.response.json()
196
+ if data.get("error") or data.get("code") == 1:
197
+ msg = data.get("error", {}).get("message") or data.get("message")
198
+ if re.search(r"agicto", msg, re.IGNORECASE):
199
+ if api_key in API_KEYS:
200
+ API_KEYS.remove(api_key)
201
+ REVOKE_KEYS.append(api_key)
202
+ continue
203
+
204
+ raise exceptions.BadRequest(msg)
205
+ except json.JSONDecodeError:
206
+ pass
207
+
208
+ if i == max_retries - 1:
209
+ raise exceptions.ServiceUnavailable
210
+
211
+ init = True
212
+ for line in r.iter_lines():
213
+ line: bytes
214
+ if line:
215
+ try:
216
+ data: dict = json.loads(line.decode().removeprefix("data: "))
217
+ except json.JSONDecodeError:
218
+ continue
219
+
220
+ if choices := data.get("choices"):
221
+ delta: dict = choices[0]["delta"]
222
+ if delta := delta.get("content"):
223
+ if init:
224
+ init = False
225
+ yield ""
226
+ yield delta
227
+
228
+ if init:
229
+ raise exceptions.ServiceUnavailable
230
+
231
+
232
+ if __name__ == "__main__":
233
+ print(generate_api_key())
234
+ exit(0)
235
+
236
+ increase_api_keys(per_thread=1)
237
+ while True:
238
+ if len(API_KEYS) == 0:
239
+ print("waiting for api key...")
240
+ time.sleep(1)
241
+ else:
242
+ print(f"{len(API_KEYS)} api keys available")
243
+ break
244
+
245
+ data = {
246
+ "model": "claude-3-opus-20240229",
247
+ "messages": [
248
+ {"role": "user", "content": "who are you?"},
249
+ ],
250
+ }
251
+
252
+ print("generating...")
253
+ for i in chat_completion_stream(**data):
254
+ print(i, end="")
app.py ADDED
@@ -0,0 +1,373 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import (
2
+ Flask,
3
+ Response,
4
+ request,
5
+ jsonify,
6
+ redirect,
7
+ url_for,
8
+ render_template,
9
+ stream_with_context,
10
+ )
11
+ from werkzeug import exceptions
12
+
13
+ import os
14
+ import logging
15
+
16
+ exceptions.BadRequestKeyError.show_exception = True
17
+
18
+ from werkzeug.middleware.proxy_fix import ProxyFix
19
+
20
+ app = Flask(__name__, static_folder=None)
21
+ app.json.sort_keys = False
22
+
23
+ app.wsgi_app = ProxyFix(app.wsgi_app)
24
+
25
+ from providers.agicto import (
26
+ chat_completion,
27
+ chat_completion_stream,
28
+ increase_api_keys,
29
+ API_KEYS,
30
+ REVOKE_KEYS,
31
+ )
32
+
33
+ # from providers.devcto import (
34
+ # chat_completion_stream,
35
+ # increase_api_keys,
36
+ # API_KEYS,
37
+ # REVOKE_KEYS,
38
+ # )
39
+
40
+
41
+ @app.route("/")
42
+ def root():
43
+ return redirect(url_for("base_url"))
44
+
45
+
46
+ import humanize
47
+ import time
48
+
49
+ UPTIME = time.time()
50
+ WAITING = UPTIME
51
+ LAST_UNAVAILABLE = None
52
+
53
+ PROMPTERS = 0
54
+ TOKENS = {"input": 0, "output": 0}
55
+
56
+ RIDDLE_ID = os.environ.get("RIDDLE_ID", "-")
57
+ RIDDLE_EN = os.environ.get("RIDDLE_EN", "-")
58
+ RIDDLE_EXPIRE = os.environ.get("RIDDLE_EXPIRE", "?")
59
+
60
+ SONG = {
61
+ "title": os.environ.get("SONG_TITLE", "-"),
62
+ "url": os.environ.get("SONG_URL", None),
63
+ }
64
+
65
+
66
+ @app.route("/v1")
67
+ def base_url():
68
+ wait = max(0, WAITING - time.time())
69
+ info = {
70
+ "base_url": url_for("base_url", _external=True),
71
+ "api_key": "(riddle_word)",
72
+ "endpoints": [str(_) for _ in app.url_map.iter_rules()],
73
+ "usages": {
74
+ "tokens": {
75
+ "input": f"{humanize.intword(TOKENS['input'])} ($0.015 / 1k)",
76
+ "output": f"{humanize.intword(TOKENS['output'])} ($0.075 / 1k)",
77
+ "cost": f"${((TOKENS['input'] * 0.015) + (TOKENS['output'] * 0.075)) / 1000:,.2f}",
78
+ },
79
+ "keys": f"{len(API_KEYS)} stored | {len(REVOKE_KEYS)} revoked",
80
+ },
81
+ "details": {
82
+ "uptime": humanize.naturaldelta(time.time() - UPTIME),
83
+ "prompters": max(0, PROMPTERS),
84
+ "waiting": humanize.naturaldelta(wait) if wait else None,
85
+ "last_pantun": (
86
+ humanize.naturaltime(time.time() - LAST_UNAVAILABLE)
87
+ if LAST_UNAVAILABLE
88
+ else None
89
+ ),
90
+ },
91
+ }
92
+
93
+ if (
94
+ request.accept_mimetypes.accept_json
95
+ and not request.accept_mimetypes.accept_html
96
+ ):
97
+ return Response(json.dumps(info, indent=2), mimetype="application/json")
98
+
99
+ return render_template(
100
+ "index.html",
101
+ prompt=json.dumps(info, indent=2),
102
+ riddle=[RIDDLE_ID, RIDDLE_EN],
103
+ riddle_expire=RIDDLE_EXPIRE,
104
+ song=SONG,
105
+ )
106
+
107
+
108
+ @app.teardown_request
109
+ def teardown_request(_):
110
+ # only from messages
111
+ if request.endpoint == "messages":
112
+ global PROMPTERS, WAITING
113
+ if PROMPTERS > 0:
114
+ PROMPTERS -= 1
115
+
116
+
117
+ @app.route("/v1/models")
118
+ def models():
119
+ return jsonify(
120
+ {
121
+ "objects": "list",
122
+ "data": [
123
+ {
124
+ "id": "claude-3-opus-20240229",
125
+ "object": "chat.completion",
126
+ "created": None,
127
+ "owned_by": "anthropic",
128
+ }
129
+ ],
130
+ }
131
+ )
132
+
133
+
134
+ API_KEY = [api_key.strip() for api_key in os.environ.get("API_KEY", "").split(",")]
135
+
136
+ from marshmallow import schema, fields, validate, validates, ValidationError, EXCLUDE
137
+
138
+ import random
139
+ import string
140
+ import json
141
+
142
+ import tiktoken
143
+
144
+
145
+ def count_tokens(input, output):
146
+ cl100k = tiktoken.get_encoding("cl100k_base")
147
+ input, output = len(cl100k.encode(input)), len(cl100k.encode(output))
148
+
149
+ global TOKENS
150
+ TOKENS["input"] += input
151
+ TOKENS["output"] += output
152
+
153
+ return {"input_tokens": input, "output_tokens": output}
154
+
155
+
156
+ class MessageSchema(schema.Schema):
157
+ model = fields.Str()
158
+ messages = fields.List(
159
+ fields.Nested(
160
+ {
161
+ "role": fields.Str(
162
+ required=True,
163
+ validate=validate.OneOf(["user", "assistant"]),
164
+ ),
165
+ "content": fields.Raw(required=True),
166
+ }
167
+ ),
168
+ required=True,
169
+ )
170
+ max_tokens = fields.Int(validate=validate.Range(min=1, max=4096))
171
+ system = fields.Str(allow_none=True)
172
+ stream = fields.Bool(allow_none=True)
173
+ temperature = fields.Float(
174
+ validate=validate.Range(min=0.0, max=1.0), allow_none=True
175
+ )
176
+ # top_k = fields.Float(allow_nan=True)
177
+ top_p = fields.Float(allow_nan=True)
178
+
179
+ @validates("model")
180
+ def validate_model(self, value):
181
+ if value not in ["claude-3-opus-20240229"]:
182
+ raise exceptions.BadRequest("Model must be claude-3-opus-20240229.")
183
+
184
+ class Meta:
185
+ unknown = EXCLUDE
186
+
187
+
188
+ @app.route("/v1/messages", methods=["POST"])
189
+ def messages():
190
+ if request.headers.get("x-api-key") not in API_KEY:
191
+ raise exceptions.Unauthorized(
192
+ "Invalid api key, open and check base_url (%s) for more info."
193
+ % url_for("base_url", _external=True)
194
+ )
195
+
196
+ if WAITING > time.time():
197
+ raise exceptions.TooManyRequests(
198
+ "Waiting for %s, and try again."
199
+ % humanize.naturaldelta(WAITING - time.time())
200
+ )
201
+
202
+ try:
203
+ data: dict = MessageSchema().load(request.json)
204
+ except ValidationError as err:
205
+ raise exceptions.BadRequestKeyError(err.messages)
206
+
207
+ if data.get("system"):
208
+ data["messages"].insert(0, {"role": "system", "content": data.pop("system")})
209
+
210
+ head = {
211
+ "id": "msg_" + "".join(random.choices(string.hexdigits, k=15)),
212
+ "model": data["model"],
213
+ "type": "message",
214
+ "role": "assistant",
215
+ "stop_sequence": None,
216
+ }
217
+
218
+ # increase prompter
219
+ global PROMPTERS
220
+ PROMPTERS += 1
221
+
222
+ input = "".join([m["content"] for m in data["messages"]])
223
+
224
+ if data.get("stream"):
225
+ next(completion := chat_completion_stream(**data))
226
+
227
+ def chunk(data):
228
+ return "data: " + json.dumps(data, separators=(",", ":")) + "\n\n"
229
+
230
+ def generate():
231
+ yield bytes("event: message_start\n", "utf-8")
232
+ yield bytes(
233
+ chunk(
234
+ {
235
+ "type": "message_start",
236
+ "message": {
237
+ "content": [],
238
+ **head,
239
+ "stop_reason": None,
240
+ "usage": {
241
+ "input_tokens": None,
242
+ "output_tokens": None,
243
+ },
244
+ },
245
+ }
246
+ ),
247
+ "utf-8",
248
+ )
249
+ yield bytes("event: content_block_start\n", "utf-8")
250
+ yield bytes(
251
+ chunk(
252
+ {
253
+ "type": "content_block_start",
254
+ "index": 0,
255
+ "content_block": {"type": "text", "text": ""},
256
+ }
257
+ ),
258
+ "utf-8",
259
+ )
260
+
261
+ output = ""
262
+ for _ in completion:
263
+ yield bytes("event: content_block_delta\n", "utf-8")
264
+ yield bytes(
265
+ chunk(
266
+ {
267
+ "type": "content_block_delta",
268
+ "index": 0,
269
+ "delta": {"type": "text_delta", "text": _},
270
+ }
271
+ ),
272
+ "utf-8",
273
+ )
274
+
275
+ output += _
276
+
277
+ yield bytes("event: content_block_stop\n", "utf-8")
278
+ yield bytes(
279
+ chunk({"type": "content_block_stop", "index": 0}),
280
+ "utf-8",
281
+ )
282
+
283
+ yield bytes("event: message_delta\n", "utf-8")
284
+ yield bytes(
285
+ chunk(
286
+ {
287
+ "type": "message_delta",
288
+ "delta": {"stop_reason": "end_turn", "stop_sequence": None},
289
+ "usage": count_tokens(input, output),
290
+ }
291
+ ),
292
+ "utf-8",
293
+ )
294
+
295
+ yield bytes("event: message_stop\n", "utf-8")
296
+ yield bytes(chunk({"type": "message_stop"}), "utf-8")
297
+
298
+ return Response(stream_with_context(generate()), mimetype="text/event-stream")
299
+
300
+ output = chat_completion(**data)
301
+ return jsonify(
302
+ {
303
+ "content": [
304
+ {
305
+ "text": output,
306
+ "type": "text",
307
+ }
308
+ ],
309
+ **head,
310
+ "stop_reason": "end_turn",
311
+ "usage": count_tokens(input, output),
312
+ }
313
+ )
314
+
315
+
316
+ import re
317
+ import traceback
318
+
319
+
320
+ @app.errorhandler(Exception)
321
+ def handle_exception(_):
322
+ traceback.print_exc()
323
+ return handle_exception(exceptions.InternalServerError())
324
+
325
+
326
+ UNAVAILABLE_MESSAGES = [
327
+ "Burung kenari terbang tinggi, Di taman bunga menari-nari. Kamu refresh gak ada henti, Tapi server-nya malah pergi!",
328
+ "Ke laut mancing ikan kerapu, Dapat satu langsung dilempar. Mau akses disuruh nunggu, Muncul service unavailable bikin kesal!",
329
+ "Beli donat rasa durian, Dimakan ramai-ramai di taman. Service unavailable datangnya seharian, Bikin hati jadi gak karuan!",
330
+ "Ke pasar beli terasi, Naik motor sambil bernyanyi. Server sibuk, coba lagi, Service unavailable nih hari!",
331
+ "Pagi-pagi minum teh manis, Disruput sambil lihat layar. Server down, jadi nangis, Error lagi, kapan lancar?",
332
+ "Pergi ke taman sambil joget, Liat kupu-kupu terbang santai. Mau refresh sampe capek, Servernya malah bilang bye-bye!",
333
+ "Ke pasar beli jeruk Bali, Eh ketemu sama teman lama. Klik refresh terus berkali-kali, Tapi servernya malah drama!",
334
+ ]
335
+
336
+
337
+ @app.errorhandler(exceptions.ServiceUnavailable)
338
+ def handle_unavailable(e: exceptions.ServiceUnavailable):
339
+ global LAST_UNAVAILABLE, WAITING
340
+ LAST_UNAVAILABLE = time.time()
341
+ WAITING = time.time() + random.randint(3, 10)
342
+ return handle_exception(
343
+ exceptions.ServiceUnavailable(random.choice(UNAVAILABLE_MESSAGES))
344
+ )
345
+
346
+
347
+ @app.errorhandler(exceptions.HTTPException)
348
+ def handle_exception(e: exceptions.HTTPException):
349
+ error = {
350
+ "error": {
351
+ "type": re.sub(r"([a-z])([A-Z])", r"\1_\2", e.__class__.__name__).lower(),
352
+ "message": e.description,
353
+ "code": e.code,
354
+ }
355
+ }
356
+ return jsonify(error), e.code
357
+
358
+
359
+ if not app.debug:
360
+ from apscheduler.schedulers.background import BackgroundScheduler
361
+
362
+ scheduler = BackgroundScheduler()
363
+ scheduler.add_job(increase_api_keys, "interval", minutes=1)
364
+ scheduler.start()
365
+
366
+ import atexit
367
+
368
+ atexit.register(lambda: scheduler.shutdown())
369
+ else:
370
+ logging.basicConfig(level=logging.DEBUG)
371
+
372
+ # testing
373
+ increase_api_keys(per_thread=1)
index.html ADDED
@@ -0,0 +1,169 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
7
+ <title>Opus :3</title>
8
+
9
+ <script src="https://cdn.tailwindcss.com"></script>
10
+ </head>
11
+
12
+ <body class="bg-gradient-to-b from-orange-900 to-black text-white select-none">
13
+ <div class="flex sm:justify-center sm:items-center min-h-screen">
14
+ <div class="sm:w-1/2 p-4">
15
+ <div>
16
+ <style>
17
+ .blink-container {
18
+ display: inline-block;
19
+ position: relative;
20
+ width: 1em;
21
+ height: 1em;
22
+ }
23
+
24
+ .blink-container::before,
25
+ .blink-container::after {
26
+ content: 'πŸ˜—';
27
+ position: absolute;
28
+ top: 0;
29
+ left: 0;
30
+ width: 100%;
31
+ height: 100%;
32
+ display: flex;
33
+ justify-content: center;
34
+ align-items: center;
35
+ }
36
+
37
+ .blink-container::after {
38
+ content: 'πŸ˜™';
39
+ opacity: 0;
40
+ animation: blink 3s infinite;
41
+ }
42
+
43
+ .blink-container:hover::before,
44
+ .blink-container:hover::after {
45
+ content: '😚';
46
+ }
47
+
48
+ @keyframes blink {
49
+
50
+ 0%,
51
+ 90% {
52
+ opacity: 0;
53
+ }
54
+
55
+ 91%,
56
+ 100% {
57
+ opacity: 1;
58
+ }
59
+ }
60
+ </style>
61
+ <h1 class="text-4xl font-bold tracking-wider">
62
+ dumb-<span class="blink-container mx-1 -mb-1"></span>pus
63
+ </h1>
64
+
65
+ <p class="mt-4">
66
+ Claude 3 Opus is a language model developed by Anthropic that is considered to be their most
67
+ capable.
68
+ </p>
69
+ </div>
70
+
71
+ <!-- riddle word -->
72
+ <div class="mt-8">
73
+ <p class="font-bold">Riddle <sup class="bg-white/10 px-1 rounded">{{ riddle_expire }}</sup> :</p>
74
+ <!-- button ID | EN -->
75
+ <div class="mt-2 flex gap-4">
76
+ <label class="flex items-center">
77
+ <input type="radio" class="mr-2" name="language" value="id" />
78
+ ID
79
+ </label>
80
+ <label class="flex items-center">
81
+ <input type="radio" class="mr-2" name="language" value="en" checked />
82
+ EN
83
+ </label>
84
+ </div>
85
+ <script>
86
+ const radioButtons = document.querySelectorAll('input[name="language"]');
87
+ radioButtons.forEach((radio) => {
88
+ radio.addEventListener('change', (event) => {
89
+ if (event.target.value === 'id') {
90
+ document.getElementById('riddle_id').classList.remove('hidden');
91
+ document.getElementById('riddle_en').classList.add('hidden');
92
+ } else {
93
+ document.getElementById('riddle_id').classList.add('hidden');
94
+ document.getElementById('riddle_en').classList.remove('hidden');
95
+ }
96
+ });
97
+ });
98
+ </script>
99
+
100
+ <div class="mt-4 cursor-help">
101
+ <span class="text-xs text-white/30 flex items-center gap-1 py-2">
102
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
103
+ stroke="currentColor" class="w-4 h-4">
104
+ <path stroke-linecap="round" stroke-linejoin="round"
105
+ d="m21 7.5-9-5.25L3 7.5m18 0-9 5.25m9-5.25v9l-9 5.25M3 7.5l9 5.25M3 7.5v9l9 5.25m0-9v9" />
106
+ </svg>
107
+ AI Generated
108
+ </span>
109
+ <pre id="riddle_id" class="font-sans hidden">{{ riddle[0] }}</pre>
110
+ <pre id="riddle_en" class="font-sans">{{ riddle[1] }}</pre>
111
+ </div>
112
+ </div>
113
+
114
+ {% if song.url %}
115
+ <div class="mt-4 max-w-2xl relative">
116
+ <button class="absolute top-0 -right-1.5 p-2 bg-white text-black rounded-full"
117
+ onclick="this.parentElement.remove()">
118
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
119
+ stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-4 h-4">
120
+ <line x1="18" y1="6" x2="6" y2="18"></line>
121
+ <line x1="6" y1="6" x2="18" y2="18"></line>
122
+ </svg>
123
+ </button>
124
+ <fieldset class="border rounded-lg p-4">
125
+ <legend class="mx-2 px-2">ads</legend>
126
+ <p class="text-center">{{ song.title }}</p>
127
+ <audio controls autoplay class="w-full my-2">
128
+ <source src="{{ song.url }}" type="audio/mpeg">
129
+ </audio>
130
+ </fieldset>
131
+ </div>
132
+ {% endif %}
133
+
134
+ <div class="mt-8 select-text">
135
+ <pre id="prompt" class="text-white">
136
+ {%- if prompt -%}
137
+ {{ prompt }}
138
+ {%- endif -%}
139
+ </pre>
140
+ </div>
141
+
142
+ <footer class="mt-6">
143
+ <p class="text-sm text-white/50">
144
+ made with 🐈 by <span class="font-bold">dakunesu</span>, support me on
145
+ <a class="font-bold" target="_blank" href="https://sociabuzz.com/dakunesu/support">
146
+ <span class="text-green-500">socia</span><span class="text-blue-500">buzz</span>
147
+ </a>
148
+ </p>
149
+ </footer>
150
+ </div>
151
+ </div>
152
+
153
+ <script>
154
+ const url = new URL(window.location.href);
155
+ // every 10 seconds, fetch info
156
+ setInterval(() => {
157
+ fetch(url, { headers: { "accept": "application/json" } })
158
+ .then((res) => res.text())
159
+ .then((text) => {
160
+ document.getElementById("prompt").innerHTML = text;
161
+ })
162
+ .catch((err) => {
163
+ console.log(err);
164
+ });
165
+ }, 10000);
166
+ </script>
167
+ </body>
168
+
169
+ </html>