Начинающий
- Статус
- Оффлайн
- Регистрация
- 14 Дек 2024
- Сообщения
- 32
- Реакции
- 0
- Выберите загрузчик игры
- Fabric
ну короче сливаю свой $elf code irc чат
это пайтон сервер, его заливаете на хостинг ниже инструкция и тама еще не доделаный клоуд конфиг
потом меняем
ScreenClickGUI на это
и добавляем файл ChatService
pythonanywhere.com
регаемся и попадаем на
это пайтон сервер, его заливаете на хостинг ниже инструкция и тама еще не доделаный клоуд конфиг
server:
from flask import Flask, request, Response, jsonify
import time
import threading
import queue
import json
import os
from pathlib import Path
app = Flask(__name__)
# Папка для хранения облачных конфигов
CONFIGS_ROOT = Path("configs")
CONFIGS_ROOT.mkdir(exist_ok=True)
class MessengerServer:
def __init__(self):
self.clients = {} # {client_id: {'queue': queue, 'username': username, 'last_seen': time}}
self.client_id_counter = 0
self.lock = threading.Lock()
self.messages = [] # храним последние 50 сообщений
def add_client(self, username):
with self.lock:
self.client_id_counter += 1
client_id = self.client_id_counter
self.clients[client_id] = {
'queue': queue.Queue(),
'username': username,
'last_seen': time.time()
}
return client_id
def remove_client(self, client_id):
with self.lock:
return self.clients.pop(client_id, None)
def broadcast(self, message):
self.messages.append(message)
if len(self.messages) > 50:
self.messages.pop(0)
disconnected = []
with self.lock:
clients_copy = dict(self.clients)
for client_id, client_data in clients_copy.items():
try:
client_data['queue'].put_nowait(message)
client_data['last_seen'] = time.time()
except:
disconnected.append(client_id)
with self.lock:
for client_id in disconnected:
self.clients.pop(client_id, None)
def cleanup_old_clients(self):
current_time = time.time()
disconnected = []
with self.lock:
for client_id, client_data in list(self.clients.items()):
if current_time - client_data['last_seen'] > 30:
disconnected.append(client_id)
for client_id in disconnected:
with self.lock:
if client_id in self.clients:
username = self.clients[client_id]['username']
self.clients.pop(client_id)
self.broadcast(f"SYSTEM:{username} покинул сеть (таймаут)")
server = MessengerServer()
# Поток очистки неактивных клиентов
def cleanup_thread():
while True:
time.sleep(10)
server.cleanup_old_clients()
threading.Thread(target=cleanup_thread, daemon=True).start()
@app.route('/')
def index():
return '''
<!DOCTYPE html>
<html>
<head>
<title>Chat Server</title>
<style>
body { font-family: Arial; margin: 20px; background: #111; color: #eee; }
#messages { height: 400px; overflow-y: scroll; border: 1px solid #444; padding: 10px; background: #1a1a1a; }
#input { margin-top: 10px; }
button { padding: 8px 16px; background: #0066ff; border: none; color: white; cursor: pointer; }
</style>
</head>
<body>
<h1>Chat Server Running</h1>
<p>API endpoints:</p>
<ul>
<li>GET /api/history — история сообщений</li>
<li>POST /api/send/<username> — отправить сообщение</li>
<li>GET /api/stream/<username> — SSE поток сообщений</li>
<li>GET /api/configs/<username> — список конфигов</li>
<li>GET/POST/DELETE /api/config/<username>/<config_name> — работа с конфигами</li>
</ul>
<h2>Тестовый чат</h2>
<div id="messages"></div>
<div id="input">
<input type="text" id="message" placeholder="Сообщение..." style="width:300px; padding:8px;">
<button onclick="sendMessage()">Отправить</button>
</div>
<script>
const username = 'web_test_' + Math.random().toString(36).substr(2, 5);
const eventSource = new EventSource(`/api/stream/${username}`);
eventSource.onmessage = function(event) {
const messages = document.getElementById('messages');
messages.innerHTML += '<div>' + event.data + '</div>';
messages.scrollTop = messages.scrollHeight;
};
function sendMessage() {
const msg = document.getElementById('message').value.trim();
if (msg) {
fetch(`/api/send/${username}`, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({message: msg})
});
document.getElementById('message').value = '';
}
}
document.getElementById('message').addEventListener('keypress', e => {
if (e.key === 'Enter') sendMessage();
});
</script>
</body>
</html>
'''
@app.route('/api/history')
def get_history():
return jsonify(server.messages)
@app.route('/api/send/<username>', methods=['POST'])
def send_message(username):
data = request.get_json(silent=True)
if not data or 'message' not in data:
return jsonify({'error': 'Invalid or missing message'}), 400
message = data['message'].strip()
if not message:
return jsonify({'error': 'Empty message'}), 400
server.broadcast(f"{username}:{message}")
return jsonify({'status': 'ok'})
@app.route('/api/stream/<username>')
def stream_messages(username):
client_id = server.add_client(username)
# Отправляем последние 20 сообщений при подключении
for msg in server.messages[-20:]:
if msg:
yield f"data: {msg}\n\n"
# Системное сообщение о входе
server.broadcast(f"SYSTEM:{username} вошел в сеть")
def generate():
queue_obj = server.clients.get(client_id, {}).get('queue')
if not queue_obj:
return
try:
while True:
try:
message = queue_obj.get(timeout=25)
yield f"data: {message}\n\n"
except queue.Empty:
yield ": heartbeat\n\n" # keep-alive
except GeneratorExit:
left = server.remove_client(client_id)
if left:
server.broadcast(f"SYSTEM:{left['username']} покинул сеть")
return Response(
generate(),
mimetype='text/event-stream',
headers={
'Cache-Control': 'no-cache',
'X-Accel-Buffering': 'no',
'Access-Control-Allow-Origin': '*'
}
)
@app.route('/api/status')
def status():
with server.lock:
return jsonify({
'clients': len(server.clients),
'messages': len(server.messages),
'clients_list': [c['username'] for c in server.clients.values()]
})
# ────────────────────────────────────────────────
# Эндпоинты для облачных конфигов
# ────────────────────────────────────────────────
@app.route('/api/configs/<username>', methods=['GET'])
def list_configs(username):
user_dir = CONFIGS_ROOT / username
if not user_dir.exists():
return jsonify([])
configs = [
f.stem for f in user_dir.iterdir()
if f.is_file() and f.suffix == '.json'
]
return jsonify(configs)
@app.route('/api/config/<username>/<config_name>', methods=['GET'])
def get_config(username, config_name):
path = CONFIGS_ROOT / username / f"{config_name}.json"
if not path.exists():
return jsonify({"error": "Config not found"}), 404
with open(path, 'r', encoding='utf-8') as f:
try:
data = json.load(f)
return jsonify(data)
except json.JSONDecodeError:
return jsonify({"error": "Invalid JSON"}), 400
@app.route('/api/config/<username>/<config_name>', methods=['POST'])
def save_config(username, config_name):
path = CONFIGS_ROOT / username / f"{config_name}.json"
path.parent.mkdir(parents=True, exist_ok=True)
data = request.get_json(silent=True)
if data is None:
return jsonify({"error": "Invalid JSON"}), 400
try:
with open(path, 'w', encoding='utf-8') as f:
json.dump(data, f, indent=2, ensure_ascii=False)
return jsonify({"status": "saved"})
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/config/<username>/<config_name>', methods=['DELETE'])
def delete_config(username, config_name):
path = CONFIGS_ROOT / username / f"{config_name}.json"
if path.exists():
path.unlink()
return jsonify({"status": "deleted"})
return jsonify({"error": "Config not found"}), 404
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8000, debug=True)
ScreenClickGUI на это
clickhui:
package com.nicside.client.ui.clickgui;
import com.nicside.api.utils.math.MouseUtil;
import lombok.Getter;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.text.Text;
import org.joml.Vector4f;
import org.lwjgl.glfw.GLFW;
import com.nicside.api.module.Category;
import com.nicside.api.system.interfaces.QuickImports;
import com.nicside.api.utils.animation.AnimationUtil;
import com.nicside.api.utils.animation.Easing;
import com.nicside.api.utils.color.UIColors;
import com.nicside.api.utils.render.RenderUtil;
import com.nicside.api.utils.render.fonts.Fonts;
import com.nicside.client.services.RenderService;
import com.nicside.client.services.ChatService;
import com.nicside.client.ui.theme.ThemeEditor;
import java.awt.Color;
import java.util.ArrayList;
import java.util.List;
@Getter
public class ScreenClickGUI extends Screen implements QuickImports {
@Getter private static final ScreenClickGUI instance = new ScreenClickGUI();
private float scroll;
private final AnimationUtil scrollAnimation = new AnimationUtil();
private boolean open;
private final AnimationUtil openAnimation = new AnimationUtil();
private final List<Panel> panels = new ArrayList<>();
private float chatX = 10, chatY = 100;
private boolean draggingChat = false;
private float dragX, dragY;
private String currentInput = "";
private boolean chatFocused = false;
public ScreenClickGUI() {
super(Text.of(""));
for (Category category : Category.values()) {
panels.add(new Panel(category));
}
ChatService.getInstance().connect();
}
@override public void close() { ThemeEditor.getInstance().save(false); open = false; super.close(); }
@override protected void init() { ThemeEditor.getInstance().init(); open = true; super.init(); }
@override
public void render(DrawContext context, int mouseX, int mouseY, float delta) {
super.render(context, mouseX, mouseY, delta);
scrollAnimation.update();
scrollAnimation.run(scroll, 600, Easing.EXPO_OUT);
openAnimation.update();
openAnimation.run(open ? 1.0 : 0.0, 500, Easing.EXPO_OUT);
float openAnim = (float) openAnimation.getValue();
if (!open && openAnim < 0.1f) { close(); return; }
int alpha = (int) (openAnim * 255);
if (draggingChat) {
chatX = mouseX - dragX;
chatY = mouseY - dragY;
}
// Рендер панелей с поддержкой описаний
renderPanels(context, mouseX, mouseY, delta, openAnim);
renderMessengerSection(context, alpha);
ThemeEditor.getInstance().render(context, mouseX, mouseY, delta);
}
private void renderMessengerSection(DrawContext context, int alpha) {
MatrixStack matrices = context.getMatrices();
float w = 150, h = 180;
RenderUtil.BLUR_RECT.draw(matrices, chatX, chatY, w, h, new Vector4f(8f), UIColors.blur(alpha));
Fonts.PS_BOLD.drawText(matrices, "NETWORK", chatX + 8, chatY + 8, 7f, UIColors.textColor(alpha));
float msgY = chatY + h - 35;
List<String> history = ChatService.getInstance().getHistory();
RenderUtil.OTHER.pushScissor(matrices, chatX, chatY + 20, w, h - 45);
for (int i = history.size() - 1; i >= 0; i--) {
Fonts.PS_REGULAR.drawText(matrices, history.get(i), chatX + 8, msgY, 5.5f, UIColors.textColor(alpha));
msgY -= 11;
}
RenderUtil.OTHER.popScissor();
RenderUtil.RECT.draw(matrices, chatX + 5, chatY + h - 20, w - 10, 15, 4f, chatFocused ? new Color(0,0,0,180) : new Color(0,0,0,100));
String display = currentInput + (chatFocused && (System.currentTimeMillis() / 500 % 2 == 0) ? "_" : "");
Fonts.PS_REGULAR.drawText(matrices, currentInput.isEmpty() && !chatFocused ? "Type..." : display, chatX + 10, chatY + h - 15, 6f, UIColors.textColor(alpha));
}
private void renderPanels(DrawContext context, int mouseX, int mouseY, float delta, float openAnim) {
float windowWidth = mc.getWindow().getScaledWidth();
float windowHeight = mc.getWindow().getScaledHeight();
float off = RenderService.getInstance().scaled(12f);
float totalW = panels.stream().map(Panel::getWidth).reduce(0f, Float::sum) + (panels.size() - 1) * (off / 2f);
float pY = (windowHeight / 12f + (float) scrollAnimation.getValue()) * openAnim + windowHeight * (!open ? 1f - openAnim : -1f + openAnim);
float startX = (windowWidth - totalW) / 2.8f;
for (Panel p : panels) {
p.setAlpha(openAnim);
p.setY(pY);
p.setX(startX + panels.indexOf(p) * (p.getWidth() + off / 2f));
p.render(context, mouseX, mouseY, delta);
}
}
@override
public boolean mouseClicked(double mouseX, double mouseY, int button) {
if (button == 0 && MouseUtil.isHovered(mouseX, mouseY, chatX, chatY, 150, 20)) {
draggingChat = true;
dragX = (float) (mouseX - chatX); dragY = (float) (mouseY - dragY);
return true;
}
chatFocused = MouseUtil.isHovered(mouseX, mouseY, chatX + 5, chatY + 180 - 20, 140, 15);
panels.forEach(p -> p.mouseClicked(mouseX, mouseY, button));
return super.mouseClicked(mouseX, mouseY, button);
}
@override
public boolean mouseReleased(double mouseX, double mouseY, int button) {
draggingChat = false;
return super.mouseReleased(mouseX, mouseY, button);
}
@override
public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
if (chatFocused) {
if (keyCode == GLFW.GLFW_KEY_ENTER && !currentInput.isEmpty()) {
ChatService.getInstance().sendMessage(currentInput);
currentInput = "";
} else if (keyCode == GLFW.GLFW_KEY_BACKSPACE && !currentInput.isEmpty()) {
currentInput = currentInput.substring(0, currentInput.length() - 1);
} else if (keyCode == GLFW.GLFW_KEY_ESCAPE) chatFocused = false;
return true;
}
if (keyCode == GLFW.GLFW_KEY_ESCAPE) { open = false; mc.mouse.lockCursor(); return true; }
panels.forEach(p -> p.keyPressed(keyCode, scanCode, modifiers));
return super.keyPressed(keyCode, scanCode, modifiers);
}
@override
public boolean charTyped(char chr, int modifiers) {
if (chatFocused) { currentInput += chr; return true; }
return ThemeEditor.getInstance().charTyped(chr, modifiers);
}
}
и добавляем файл ChatService
chatservise:
package com.nicside.client.services;
import java.io.*;
import java.net.*;
import java.util.*;
import java.util.concurrent.*;
import com.google.gson.*;
public class ChatService {
private static final ChatService instance = new ChatService();
public static ChatService getInstance() { return instance; }
private final List<String> history = Collections.synchronizedList(new ArrayList<>());
private final String pcUser = System.getProperty("user.name").replaceAll("[^a-zA-Z0-9]", "_"); // safe username
private final String baseUrl = "тут ваш домен с пайтон ани вхере";
private Thread messageListener;
private volatile boolean running = false;
private final Gson gson = new Gson();
public String getCurrentUser() {
return pcUser;
}
public void connect() {
if (running) return;
running = true;
fetchHistory(); // initial load
startSseListener(); // real-time updates via SSE
}
private void fetchHistory() {
try {
URL url = new URL(baseUrl + "/api/history");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(6000);
conn.setReadTimeout(6000);
if (conn.getResponseCode() == 200) {
String json = readStream(conn.getInputStream());
JsonArray arr = gson.fromJson(json, JsonArray.class);
history.clear();
for (JsonElement e : arr) {
history.add(e.getAsString());
}
System.out.println("[Chat] Loaded " + history.size() + " history messages");
} else {
System.err.println("[Chat] History fetch failed: " + conn.getResponseCode());
}
conn.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
}
public void sendPrivateMessage(String type, String data, String target) {
if (target == null || target.trim().isEmpty()) {
System.err.println("[Private] No target username provided");
return;
}
String messageContent;
if ("FRIEND_REQUEST".equals(type)) {
messageContent = "[FRIEND_REQUEST]" + pcUser + ":" + data; // simple format
} else {
messageContent = "[" + type + "]" + pcUser + ": " + data;
}
// Send to the TARGET user via the only known send endpoint
try {
String safeTarget = URLEncoder.encode(target.trim(), "UTF-8");
URL url = new URL(baseUrl + "/api/send/" + safeTarget);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setDoOutput(true);
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
byte[] body = messageContent.getBytes("UTF-8");
conn.setRequestProperty("Content-Type", "text/plain; charset=utf-8");
conn.setRequestProperty("Content-Length", String.valueOf(body.length));
try (OutputStream os = conn.getOutputStream()) {
os.write(body);
os.flush();
}
int code = conn.getResponseCode();
System.out.println("[Private Send to " + target + "] Status: " + code);
if (code >= 400 && conn.getErrorStream() != null) {
System.err.println("[Private Send Error]: " + readStream(conn.getErrorStream()));
}
conn.disconnect();
} catch (Exception e) {
System.err.println("[Private Send Failed to " + target + "]: " + e.getMessage());
e.printStackTrace();
}
}
private void startSseListener() {
messageListener = new Thread(() -> {
while (running) {
try {
URL url = new URL(baseUrl + "/api/stream/" + URLEncoder.encode(pcUser, "UTF-8"));
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(10000);
conn.setReadTimeout(0); // no timeout for long-lived SSE
try (BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()))) {
String line;
while (running && (line = reader.readLine()) != null) {
if (line.startsWith("data: ")) {
String data = line.substring(6).trim();
if (!data.isEmpty() && !history.contains(data)) {
history.add(data);
System.out.println("[Chat SSE] New: " + data);
// Here you can also call UI refresh callback if you add one later
}
}
}
}
conn.disconnect();
} catch (Exception e) {
if (running) {
System.err.println("[Chat SSE] Disconnected, reconnecting in 3s... " + e.getMessage());
try { Thread.sleep(3000); } catch (InterruptedException ignored) {}
}
}
}
});
messageListener.setDaemon(true);
messageListener.start();
}
public void sendMessage(String text) {
if (text == null || (text = text.trim()).isEmpty()) return;
try {
String safeUser = URLEncoder.encode(pcUser, "UTF-8");
URL url = new URL(baseUrl + "/api/send/" + safeUser);
System.out.println("[Chat Send] → " + url);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setDoOutput(true);
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
// Самое важное — правильный Content-Type
conn.setRequestProperty("Content-Type", "application/json; charset=utf-8");
// Формируем JSON — сервер, скорее всего, ожидает что-то вроде {"message": "..."} или {"text": "..."}
// Начни с самого распространённого варианта:
JsonObject payload = new JsonObject();
payload.addProperty("message", text); // ← попробуй сначала это
// Альтернативы — если не заработает, по очереди раскомментируй одну из них вместо предыдущей:
// payload.addProperty("text", text);
// payload.addProperty("content", text);
// payload.addProperty("msg", text);
// payload.addProperty("data", text);
String jsonBody = gson.toJson(payload);
System.out.println("[Chat Send] Body: " + jsonBody);
byte[] output = jsonBody.getBytes("UTF-8");
conn.setRequestProperty("Content-Length", String.valueOf(output.length));
try (OutputStream os = conn.getOutputStream()) {
os.write(output);
os.flush();
}
int code = conn.getResponseCode();
System.out.println("[Chat Send] Status: " + code);
if (code >= 200 && code < 300) {
System.out.println("[Chat Send] Успех!");
// Можно сразу добавить отправленное сообщение локально в историю, если хочешь
// history.add(pcUser + ": " + text);
} else {
String errorMsg = "";
if (conn.getErrorStream() != null) {
errorMsg = readStream(conn.getErrorStream());
}
System.err.println("[Chat Send] Ошибка: " + code + " → " + errorMsg);
}
conn.disconnect();
} catch (Exception e) {
System.err.println("[Chat Send] Exception: " + e.getClass().getSimpleName() + " → " + e.getMessage());
e.printStackTrace();
}
}
public List<String> getHistory() {
return Collections.unmodifiableList(new ArrayList<>(history));
}
public void disconnect() {
running = false;
if (messageListener != null) {
messageListener.interrupt();
}
}
private String readStream(InputStream is) throws IOException {
StringBuilder sb = new StringBuilder();
try (BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8"))) {
String line;
while ((line = br.readLine()) != null) {
sb.append(line).append("\n");
}
}
return sb.toString().trim();
}
}
pythonanywhere.com
регаемся и попадаем на
