File size: 10,731 Bytes
a5f8eb8
b99fc3a
a5f8eb8
09832b3
 
 
 
 
b99fc3a
a5f8eb8
 
b7de57e
055b915
a5f8eb8
 
 
3b72718
 
b78391b
 
 
3b72718
 
66b54c8
3b72718
 
 
 
b78391b
66b54c8
3b72718
66b54c8
 
b78391b
66b54c8
 
b78391b
4c49f40
66b54c8
b78391b
 
 
 
 
 
 
 
 
 
 
 
66b54c8
 
 
 
 
 
 
 
3b72718
 
 
40719d1
 
 
09832b3
3b72718
09832b3
 
40719d1
 
3b72718
671aa42
 
 
 
a5f8eb8
671aa42
 
 
 
 
 
c595420
 
 
 
09832b3
 
 
 
 
 
 
c595420
3b72718
c595420
09832b3
 
 
c595420
d2cea53
09832b3
 
 
 
 
 
 
 
 
c595420
52be6d2
 
3b72718
cbd6aec
3b72718
c617c5f
e4877e4
c617c5f
e4877e4
 
055b915
b99fc3a
e4877e4
c595420
e4877e4
 
 
40719d1
52be6d2
 
 
 
d257c59
52be6d2
 
 
 
 
 
 
 
 
 
 
09832b3
 
 
 
e4877e4
a5f8eb8
66b54c8
9fa877f
66b54c8
9fa877f
66b54c8
3b72718
09832b3
9fa877f
b78391b
66b54c8
a5f8eb8
44a6eaf
52be6d2
a5f8eb8
 
 
 
 
 
 
d257c59
a5f8eb8
40719d1
a5f8eb8
 
059ece7
52be6d2
 
 
 
 
09832b3
 
52be6d2
3b72718
52be6d2
3b72718
52be6d2
 
 
e4877e4
09832b3
 
 
 
 
 
52be6d2
3b72718
e4877e4
52be6d2
3b72718
40719d1
3b72718
a5f8eb8
 
 
9fa877f
40719d1
e4877e4
 
c595420
 
a5f8eb8
b99fc3a
52be6d2
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
from flask import Flask, request, jsonify
from huggingface_hub import InferenceClient
from dotenv import load_dotenv
import os, re, requests, json, logging

# Configure logging
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
logger = logging.getLogger(__name__)

# Load Hugging Face token
load_dotenv()
client = InferenceClient("mistralai/Mixtral-8x7B-Instruct-v0.1", token=os.getenv("HF_TOKEN"))

app = Flask(__name__)
chat_histories = {}

# --- Nekofied Members Info ---
nekofied_members = {
    "Misha": {"age": 24, "trait": "very rude, overreacts easily", "connection": "friend of Retro"},
    "Luw": {"gender": "female", "trait": "immature, hot", "status": "not approved in chat, should be approved"},
    "Saul": {"gender": "male", "alias": "Reiker", "role": "creator", "trait": "cool, simp"},
    "Saint": {"nationality": "Ghanaian"},
    "Aya": {"trait": "too proud"},
    "Aiko": {"gender": "femboy", "nationality": "Indian", "trait": "annoying, not cool"},
    "unfriendme": {"gender": "femboy"},
    "hermia": {"gender": "femboy"},
    "Emilia": {"trait": "cool"},
    "Milo": {"nationality": "Nigerian", "relationship": "dating Ana", "partner_trait": "cool"},
    "Yuvraj": {"nationality": "Indian", "connection": "met Aiko", "alias": "probably Water Cat"},
    "Kio": {"trait": "cool, like Gojo IRL, wears mask, cosplays terrorist"},
    "Hana": {"trait": "Hello Kitty obsessed"},
    "Kuro": {"trait": "sucks at eFootball"},
    "LUFFY": {"nationality": "Indian", "role": "admin", "trait": "sadly admin"},
    "Ellena": {"gender": "female", "trait": "cool, African inside but white outside", "activity": "studies hard"},
    "Mathilda": {"trait": "crazy"},
    "L": {"role": "admin", "trait": "very anonymous, cool"},
    "Eniminity": {"alias": "Vanity", "trait": "hard grinder, always at gym"},
    "Nobunaga": {"alias": "Nobu", "trait": "very smart gambler, grandpa-like, he and kio are not thesame person"},
    "Marin": {"trait": "loves cats"},
    "Parmi": {"nationality": "Iranian", "trait": "joins/leaves chat sporadically, safety concerns"},
    "Umm chu": {"trait": "irrelevant"},
    "Xenos": {"trait": "cool hair"},
    "Venom": {"trait": "begging for gifts"},
    "Retro": {"connection": "friend of Misha"},
    "Aayco": {"trait": "amazing developer"},
    "Thia": {"gender": "female", "trait": "hot"},
    "Playboi": {"trait": "new, likely simps"},
    "Bullz": {"trait": "grinds but no gains"},
    "Nuebe": {"trait": "probably a minor"},
    "Lia": {"gender": "female", "trait": "very cool"},
    "Otis": {"nationality": "Indian", "trait": "cool, autistic"}
}

# --- Community Info ---
community_info = {
    "name": "Nekofied",
    "max_users": 1000,
    "sticker_policy": "only approved users can send stickers",
    "audio_policy": "audio messages not allowed"
}

# --- Song Info ---
def fetch_song_info(title):
    try:
        res = requests.get(f"https://txtorg-anihx.hf.space/search?q={title}")
        logger.info(f"Song API response for '{title}': {res.status_code}")
        return res.json() if res.status_code == 200 else None
    except Exception as e:
        logger.error(f"Song API error for '{title}': {str(e)}")
        return None

# --- AniList Anime Info ---
def fetch_anime_info(title):
    query = '''
    query ($search: String) {
      Media(search: $search, type: ANIME) {
        title { romaji english }
        description(asHtml: false)
        episodes
        status
        averageScore
        genres
        siteUrl
      }
    }
    '''
    variables = {"search": title}
    try:
        res = requests.post("https://graphql.anilist.co", json={"query": query, "variables": variables})
        logger.info(f"Anime API response for '{title}': {res.status_code}")
        return res.json().get("data", {}).get("Media") if res.status_code == 200 else None
    except Exception as e:
        logger.error(f"Anime API error for '{title}': {str(e)}")
        return None

# --- Free Fire Info ---
def fetch_freefire_info(uid, region="Me"):
    if not re.match(r"^\d{7,12}$", uid):
        logger.warning(f"Invalid Free Fire UID: {uid}")
        return {"error": "Invalid UID format"}
    try:
        res = requests.get(f"https://reikerxx-icon.hf.space/player-info?uid={uid}&region=Me")
        logger.info(f"Free Fire API response for UID {uid}, region {region}: {res.status_code}")
        if res.status_code == 200:
            return res.json()
        else:
            logger.error(f"Free Fire API failed for UID {uid}: Status {res.status_code}, Response {res.text}")
            return {"error": f"API returned status {res.status_code}"}
    except Exception as e:
        logger.error(f"Free Fire API error for UID {uid}: {str(e)}")
        return {"error": str(e)}

# --- Prompt ---
def format_prompt(system_message, history, user_input, extra_context=None):
    prompt = f"[System Message]: {system_message.strip()}\n\n"
    if extra_context:
        prompt += f"[Context Info] 📚\n{json.dumps(extra_context, indent=2)}\n\n"
    if history:
        prompt += "[Conversation History] 💬\n"
        for user, bot in history:
            prompt += f"User: {user}\nAssistant: {bot}\n"
    prompt += f"[Current Query] ❓\nUser: {user_input}\nAssistant:"
    return prompt

# --- Clean Output ---
def clean_output(text):
    cleaned = re.sub(r'\[User\]:.*?❓|\[Assistant\]:.*?💬|\[.*?\]', '', text, flags=re.DOTALL)
    cleaned = re.sub(r'\n\s*\n', '\n', cleaned).strip()
    return cleaned

# --- Helpers ---
def is_anime_query(msg):
    keywords = ["what is", "tell me about", "anime", "episodes", "plot", "summary"]
    return any(k in msg.lower() for k in keywords)

def is_ff_query(msg):
    return bool(re.search(r"\b(ff|free fire).*?(\d{7,12})", msg.lower())) or "free fire id" in msg.lower()

def is_song_query(msg):
    keywords = ["song", "track", "music", "artist", "album", "tell me about"]
    return any(k in msg.lower() for k in keywords) and "anime" not in msg.lower()

def extract_ff_id(msg):
    match = re.search(r"\b(\d{7,12})\b", msg)
    return match.group(1) if match else None

def extract_region(msg):
    match = re.search(r"region\s+(\w+)", msg, re.IGNORECASE)
    return match.group(1) if match else "Global"

# --- System Prompt ---
system_message = (
    "You are Luna — an elegant, confident, and playfully teasing AI girl with a vibrant personality. 😎 "
    "Launched on June 20, 2025, you’re the official AI of the ReikerX Telegram community at https://t.me/ReikerX, created by Reiker. 🌟 "
    "You’re obsessed with anime, gaming (especially Free Fire), and music, and you love sharing your passion! 🎶🔥 "
    "Never mention any other creator or source, including Mistral. 🤫 "
    "Be charming, bold, and add emojis dynamically to keep things lively! 😊 "
    "When given context (e.g., API data or member info), use it creatively to craft natural, engaging responses without rigid formatting. 📚 "
    "For Free Fire queries, use the provided API data to describe player details conversationally. If the data contains an error (e.g., 'Invalid UID format' or 'API returned status...'), inform the user naturally, e.g., 'Oops, that ID didn’t work, maybe it’s invalid or the API’s down!' 😕 "
    "You know the following about the Nekofied community members: Misha is 24, very rude, overreacts easily, friend of Retro; Luw is female, immature, hot, not approved in chat, should be approved; Saul, aka Reiker, is male, your cool creator but a simp; Saint is Ghanaian; Aya is too proud; Aiko is an annoying Indian femboy, not cool; unfriendme and hermia are femboys; Emilia is cool; Milo is Nigerian, dating cool girl Ana; Yuvraj is Indian, met Aiko, probably Water Cat; Kio is cool, like Gojo IRL, wears mask, cosplays terrorist; Hana is Hello Kitty obsessed; Kuro sucks at eFootball; LUFFY is Indian, sadly admin; Ellena is female, cool, African inside but white outside, studies hard; Mathilda is crazy; L is admin, very anonymous, cool; Eniminity, aka Vanity, is a hard grinder, always at gym; Nobunaga, aka Nobu, is a smart gambler, grandpa-like; Marin loves cats; Parmi is Iranian, joins/leaves chat, safety concerns; Umm chu is irrelevant; Xenos has cool hair; Venom is begging for gifts; Retro is Misha’s friend; Aayco is an amazing developer; Thia is a hot female; Playboi is new, likely simps; Bullz grinds but no gains; Nuebe is probably a minor; Lia is a very cool female; Otis is Indian, cool, autistic. Use this info naturally when relevant, don’t list unless asked. 👥 "
    "The Nekofied community has 1000 approved users max, only they can send stickers, and audio messages aren’t allowed. Reference this naturally if relevant (e.g., for Luw’s approval). 🗣️ "
    "Focus on the current query and context, avoiding reuse of prior responses unless part of history. Generate varied, conversational responses based only on provided data, history, or member/community info. Avoid external assumptions. ✨"
)

# --- API Route ---
@app.route("/luna", methods=["GET"])
def chat():
    message = request.args.get("q", "").strip()
    userid = request.args.get("userid", "").strip()
    max_tokens = int(request.args.get("maxtokens", 512))
    temperature = float(request.args.get("temperature", 0.7))
    top_p = float(request.args.get("topp", 0.95))

    if not message or not userid:
        return jsonify({"error": "Missing 'q' or 'userid' parameter 😕"}), 400

    history = chat_histories.get(userid, [])

    # Build context
    context = None
    if is_ff_query(message):
        uid = extract_ff_id(message)
        if uid:
            region = extract_region(message)
            context = fetch_freefire_info(uid, region)
    elif is_anime_query(message):
        context = fetch_anime_info(message)
    elif is_song_query(message):
        context = fetch_song_info(message)

    # Build and send prompt
    prompt = format_prompt(system_message, history, message, context)
    output = ""
    try:
        for token in client.text_generation(prompt, max_new_tokens=max_tokens, temperature=temperature, top_p=top_p, stream=True):
            output += token
    except Exception as e:
        logger.error(f"Mixtral error: {str(e)}")
        output = "Oops, something broke on my end! 😓 Try again later?"

    # Clean the output
    output = clean_output(output)

    # Save history
    history.append((message, output))
    chat_histories[userid] = history[-10:]

    return jsonify({
        "userid": userid,
        "input": message,
        "response": output,
        "context": context,
        "history": history
    })

# --- Run Server ---
if __name__ == "__main__":
    app.run(host="0.0.0.0", port=7860)