diff --git a/naticord.py b/naticord.py
index 187817f..c2eb81a 100644
--- a/naticord.py
+++ b/naticord.py
@@ -1,346 +1,180 @@
-from PyQt5.QtWidgets import QApplication, QLabel, QVBoxLayout, QWidget, QListWidget, QPushButton, \
- QTextEdit, QLineEdit, QHBoxLayout, QProgressBar, QMessageBox, QMenu, \
- QAction, QListWidgetItem, QTabWidget, QFontDialog, QInputDialog, QCheckBox, QDialog, QGridLayout, QPushButton, QSizePolicy, QScrollArea
-from PyQt5.QtCore import QTimer, Qt, pyqtSignal, QSize
-from PyQt5.QtGui import QPixmap, QIcon
import sys
-import os
-import configparser
import requests
-from threading import Thread
+import configparser
+from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QListWidget, QTextEdit, QLineEdit, QPushButton, QTabWidget, QMessageBox, QDialog, QListWidgetItem
+from PyQt5.QtGui import QIcon
+from PyQt5.QtCore import Qt, QTimer
-class Naticord(QWidget):
+class LoginScreen(QDialog):
def __init__(self):
super().__init__()
- self.setWindowTitle("Naticord")
+ self.setWindowTitle("Login")
self.layout = QVBoxLayout()
- self.config = configparser.ConfigParser()
- self.config.read('settings.ini')
- self.mode = self.config['Settings']['mode']
+ self.token_input = QLineEdit()
+ self.token_input.setPlaceholderText("Enter your Discord token...")
+ self.layout.addWidget(self.token_input)
- self.top_bar_layout = QHBoxLayout()
- self.layout.addLayout(self.top_bar_layout)
+ self.login_button = QPushButton("Login")
+ self.login_button.clicked.connect(self.login)
+ self.layout.addWidget(self.login_button)
- self.left_layout = QVBoxLayout()
+ self.setLayout(self.layout)
- self.loading_screen = QWidget()
- self.loading_screen.setFixedSize(200, 150)
- self.loading_layout = QVBoxLayout(self.loading_screen)
- self.loading_label = QLabel("Initializing...")
- self.loading_label.setAlignment(Qt.AlignCenter)
- self.loading_layout.addWidget(self.loading_label)
- self.progress_bar = QProgressBar()
- self.progress_bar.setRange(0, 100)
- self.progress_bar.setTextVisible(False)
- self.loading_layout.addWidget(self.progress_bar)
- self.left_layout.addWidget(self.loading_screen)
+ def login(self):
+ token = self.token_input.text().strip()
+ if token:
+ # Save token to config file
+ config = configparser.ConfigParser()
+ config['Auth'] = {'Token': token}
+ with open('config.ini', 'w') as configfile:
+ config.write(configfile)
+ self.accept()
+ else:
+ QMessageBox.critical(self, "Error", "Please enter a valid Discord token.")
- self.user_info_layout = QHBoxLayout()
- self.label_avatar = QLabel()
- self.user_info_layout.addWidget(self.label_avatar)
- self.left_layout.addLayout(self.user_info_layout)
+class Naticord(QWidget):
+ def __init__(self):
+ super().__init__()
+ self.setWindowTitle("Naticord")
+ self.layout = QHBoxLayout()
+ self.setLayout(self.layout)
- self.tabs = QTabWidget()
+ # Left Panel
+ self.left_panel = QWidget()
+ self.left_panel_layout = QVBoxLayout()
+ self.left_panel.setLayout(self.left_panel_layout)
self.friends_tab = QWidget()
- self.friends_layout = QVBoxLayout(self.friends_tab)
- self.friends_search_input = QLineEdit()
- self.friends_search_input.setPlaceholderText("Search Friends...")
- self.friends_search_input.textChanged.connect(self.search_friends)
- self.friends_layout.addWidget(self.friends_search_input)
- self.friends_list = QListWidget(self.friends_tab)
- self.friends_layout.addWidget(self.friends_list)
- self.friends_list.itemClicked.connect(self.load_dm)
+ self.servers_tab = QWidget()
+
+ self.tabs = QTabWidget()
self.tabs.addTab(self.friends_tab, "Friends")
+ self.tabs.addTab(self.servers_tab, "Servers")
- self.servers_tab = QWidget()
+ self.friends_layout = QVBoxLayout(self.friends_tab)
self.servers_layout = QVBoxLayout(self.servers_tab)
- self.servers_search_input = QLineEdit()
- self.servers_search_input.setPlaceholderText("Search Servers...")
- self.servers_search_input.textChanged.connect(self.search_servers)
- self.servers_layout.addWidget(self.servers_search_input)
- self.servers_list = QListWidget(self.servers_tab)
- self.servers_layout.addWidget(self.servers_list)
- self.servers_list.itemClicked.connect(self.load_channels)
- self.tabs.addTab(self.servers_tab, "Servers")
- self.left_layout.addWidget(self.tabs)
+ self.user_info_label = QLabel("User Info")
+ self.friends_label = QLabel("Friends")
+ self.servers_label = QLabel("Servers")
- self.layout.addLayout(self.left_layout)
+ self.friends_list = QListWidget()
+ self.servers_list = QListWidget()
- self.right_layout = QVBoxLayout()
- self.messages_text_edit = QTextEdit()
- self.messages_text_edit.setReadOnly(True)
- self.right_layout.addWidget(self.messages_text_edit)
+ self.friends_layout.addWidget(self.friends_label)
+ self.friends_layout.addWidget(self.friends_list)
- self.message_input = QLineEdit()
- self.message_input.returnPressed.connect(self.send_message)
- self.right_layout.addWidget(self.message_input)
+ self.servers_layout.addWidget(self.servers_label)
+ self.servers_layout.addWidget(self.servers_list)
- self.emoji_button = QPushButton("😀")
- self.emoji_button.clicked.connect(self.open_emoji_dialog)
- self.right_layout.addWidget(self.emoji_button)
+ self.left_panel_layout.addWidget(self.user_info_label)
+ self.left_panel_layout.addWidget(self.tabs)
- self.layout.addLayout(self.right_layout)
+ self.layout.addWidget(self.left_panel)
- self.setLayout(self.layout)
+ # Right Panel
+ self.right_panel = QWidget()
+ self.right_panel_layout = QVBoxLayout()
+ self.right_panel.setLayout(self.right_panel_layout)
+
+ self.messages_label = QLabel("Messages")
+ self.right_panel_layout.addWidget(self.messages_label)
+
+ self.messages_text_edit = QTextEdit()
+ self.right_panel_layout.addWidget(self.messages_text_edit)
- self.progress_step = 0
+ self.message_input = QLineEdit()
+ self.right_panel_layout.addWidget(self.message_input)
- self.label_avatar.hide()
- self.friends_list.hide()
- self.messages_text_edit.hide()
+ self.send_button = QPushButton("Send")
+ self.send_button.clicked.connect(self.send_message)
+ self.right_panel_layout.addWidget(self.send_button)
- self.login_screen = LoginScreen()
- self.login_screen.login_signal.connect(self.load_client)
- self.login_screen.show()
+ self.layout.addWidget(self.right_panel)
- self.current_channel_id = None
- self.current_server_id = None
- self.resized_once = False
+ # Load data
+ self.token = self.get_token()
+ if not self.token:
+ # Show login screen if token is not available
+ self.show_login_screen()
+ else:
+ self.load_data()
+ # Set up message refresh timer
self.refresh_timer = QTimer(self)
- self.refresh_timer.timeout.connect(self.refresh_dm)
- self.refresh_timer.start(3000)
-
- self.create_settings_menu()
-
- self.load_emojis_thread = Thread(target=self.load_emojis)
- self.load_emojis_thread.start()
-
- def create_settings_menu(self):
- self.settings_button = QPushButton()
- if self.mode == "light":
- self.settings_button.setIcon(QIcon("settings.svg"))
- elif self.mode == "dark":
- self.settings_button.setIcon(QIcon("settings_white.svg"))
- self.settings_button.setFixedSize(30, 30)
- self.settings_button.clicked.connect(self.show_settings_menu)
- self.top_bar_layout.addWidget(self.settings_button, alignment=Qt.AlignRight)
-
- self.settings_menu = QMenu(self)
- self.light_mode_action = QAction("Light Mode", self)
- self.light_mode_action.triggered.connect(self.set_light_mode)
- self.settings_menu.addAction(self.light_mode_action)
-
- self.dark_mode_action = QAction("Dark Mode", self)
- self.dark_mode_action.triggered.connect(self.set_dark_mode)
- self.settings_menu.addAction(self.dark_mode_action)
-
- self.font_selection_action = QAction("Select Font", self)
- self.font_selection_action.triggered.connect(self.select_font)
- self.settings_menu.addAction(self.font_selection_action)
-
- def show_settings_menu(self):
- self.settings_menu.popup(self.settings_button.mapToGlobal(self.settings_button.rect().bottomRight()))
-
- def set_light_mode(self):
- self.mode = "light"
- self.apply_style()
-
- def set_dark_mode(self):
- self.mode = "dark"
- self.apply_style()
-
- def apply_style(self):
- if self.mode == "light":
- self.setStyleSheet("""
- QWidget {
- background-color: white;
- color: black;
- }
- QLineEdit, QTextEdit, QListWidget {
- border: 1px solid #444;
- }
- QTabBar::tab {
- background-color: #ddd;
- }
- QTabBar::tab:selected {
- background-color: #ccc;
- }
- """)
- self.settings_button.setIcon(QIcon("settings.svg"))
- elif self.mode == "dark":
- self.setStyleSheet("""
- QWidget {
- background-color: #333;
- color: white;
- }
- QLineEdit, QTextEdit, QListWidget {
- border: 1px solid #444;
- }
- QTabBar::tab {
- background-color: #555;
- }
- QTabBar::tab:selected {
- background-color: #777;
- }
- """)
- self.settings_button.setIcon(QIcon("settings_white.svg"))
-
- self.config['Settings']['mode'] = self.mode
- with open('settings.ini', 'w') as configfile:
- self.config.write(configfile)
-
- def load_client(self, token=""):
- token_file_path = os.path.join(os.path.dirname(__file__), "token.txt")
- if not token and os.path.exists(token_file_path):
- with open(token_file_path, "r") as f:
- token = f.read().strip()
-
- if not token:
- self.login_screen.show()
+ self.refresh_timer.timeout.connect(self.refresh_messages)
+ self.refresh_timer.start(3000) # Refresh every 2 seconds
+
+ def show_login_screen(self):
+ login_screen = LoginScreen()
+ if login_screen.exec_() == QDialog.Accepted:
+ self.token = self.get_token()
+ self.load_data()
+ else:
+ sys.exit()
+
+ def get_token(self):
+ config = configparser.ConfigParser()
+ config.read('config.ini')
+ if 'Auth' in config and 'Token' in config['Auth']:
+ return config['Auth']['Token']
else:
- self.token = token
- self.login_screen.hide()
- self.loading_screen.show()
- QTimer.singleShot(1000, lambda: self.authenticate_with_token(token))
+ return None
+
+ def load_data(self):
+ self.load_user_info()
+ self.load_friends()
+ self.load_servers()
- def authenticate_with_token(self, token):
- headers = {"Authorization": f"{token}"}
+ def load_user_info(self):
+ headers = {"Authorization": f"{self.token}"}
response = requests.get("https://discord.com/api/v9/users/@me", headers=headers)
if response.status_code == 200:
user_data = response.json()
- self.loading_label.setText(f"Welcome, {user_data.get('username')}!")
- self.progress_bar.setValue(30)
- QTimer.singleShot(1000, lambda: self.update_user_info(user_data, token))
+ self.user_info_label.setText(f"User Info: {user_data.get('username')}")
else:
- QMessageBox.critical(self, "Error", "Authentication failed.")
- self.loading_screen.hide()
- self.load_client()
-
- def update_user_info(self, user_data, token):
- avatar_url = f"https://cdn.discordapp.com/avatars/{user_data.get('id')}/{user_data.get('avatar')}.png"
-
- self.loading_label.setText("Loading profile picture...")
- self.progress_bar.setValue(50)
- avatar_data = requests.get(avatar_url).content
-
- pixmap = QPixmap()
- pixmap.loadFromData(avatar_data)
- pixmap = pixmap.scaled(48, 48, Qt.KeepAspectRatio)
- self.label_avatar.setPixmap(pixmap)
- self.label_avatar.show()
-
- self.loading_label.setText("Loading friend and server lists...")
- self.progress_bar.setValue(80)
- QTimer.singleShot(1000, lambda: self.populate_friends_and_servers(token))
-
- def populate_friends_and_servers(self, token):
- headers = {"Authorization": f"{token}"}
- response = requests.get("https://discord.com/api/v9/users/@me/relationships", headers=headers)
- if response.status_code == 200:
- friends_data = response.json()
- for friend in friends_data:
- friend_name = friend.get("user", {}).get("username")
- self.friends_list.addItem(friend_name)
-
- response = requests.get("https://discord.com/api/v9/users/@me/guilds", headers=headers)
- if response.status_code == 200:
- servers_data = response.json()
- for server in servers_data:
- server_name = server.get("name")
- self.servers_list.addItem(server_name)
+ QMessageBox.warning(self, "Error", "Failed to fetch user information.")
- self.progress_bar.setValue(100)
- self.loading_label.setText("Loading complete!")
- self.loading_screen.hide()
- self.friends_list.show()
- self.servers_list.show()
-
- def load_dm(self, item):
- friend_name = item.text()
+ def load_friends(self):
headers = {"Authorization": f"{self.token}"}
response = requests.get("https://discord.com/api/v9/users/@me/channels", headers=headers)
if response.status_code == 200:
channels_data = response.json()
for channel in channels_data:
- if channel.get("type") == 1:
- recipients = channel.get("recipients", [])
- if len(recipients) == 1 and recipients[0].get("username") == friend_name:
- channel_id = channel.get("id")
- if channel_id != self.current_channel_id:
- messages = self.fetch_messages(channel_id)
- if messages:
- self.display_messages(messages)
- self.messages_text_edit.show()
- self.message_input.show()
- if not getattr(self, 'resized_once', False):
- self.resize_once()
- self.resized_once = True
- self.current_channel_id = channel_id
- else:
- QMessageBox.warning(self, "Error", f"Failed to fetch messages for {friend_name}.")
- break
+ friend_name = channel.get("recipients", [{}])[0].get("username", "Unknown")
+ item = QListWidgetItem(friend_name)
+ item.setData(Qt.UserRole, channel.get("id"))
+ self.friends_list.addItem(item)
+ self.friends_list.itemDoubleClicked.connect(self.load_channel_messages)
else:
- QMessageBox.warning(self, "Error", "Failed to fetch DM channels.")
-
- def refresh_dm(self):
- if self.current_channel_id is not None:
- messages = self.fetch_messages(self.current_channel_id)
- if messages:
- self.display_messages(messages)
+ QMessageBox.warning(self, "Error", "Failed to fetch friends.")
- def load_channels(self, item):
- server_name = item.text()
+ def load_servers(self):
headers = {"Authorization": f"{self.token}"}
- response = requests.get(f"https://discord.com/api/v9/users/@me/guilds", headers=headers)
+ response = requests.get("https://discord.com/api/v9/users/@me/guilds", headers=headers)
if response.status_code == 200:
servers_data = response.json()
for server in servers_data:
- if server.get("name") == server_name:
- self.current_server_id = server.get("id")
- response = requests.get(f"https://discord.com/api/v9/guilds/{self.current_server_id}/channels", headers=headers)
- if response.status_code == 200:
- channels_data = response.json()
- self.servers_list.clear()
- back_item = QListWidgetItem("⮜ Go Back")
- back_item.setData(Qt.UserRole, "back")
- self.servers_list.addItem(back_item)
- for channel in channels_data:
- channel_name = channel.get("name")
- self.servers_list.addItem(channel_name)
- self.servers_list.item(self.servers_list.count() - 1).setData(Qt.UserRole, channel.get("id"))
- self.servers_list.itemClicked.connect(self.load_messages)
- break
-
- def load_messages(self, item):
+ server_name = server.get("name")
+ item = QListWidgetItem(server_name)
+ item.setData(Qt.UserRole, server.get("id"))
+ self.servers_list.addItem(item)
+ self.servers_list.itemDoubleClicked.connect(self.load_channel_messages)
+ else:
+ QMessageBox.warning(self, "Error", "Failed to fetch servers.")
+
+ def load_channel_messages(self, item):
channel_id = item.data(Qt.UserRole)
- if channel_id == "back":
- self.servers_list.show()
- self.friends_list.clear()
- return
- if channel_id != self.current_channel_id:
- messages = self.fetch_messages(channel_id)
- if messages:
- self.display_messages(messages)
- self.messages_text_edit.show()
- self.message_input.show()
- if not getattr(self, 'resized_once', False):
- self.resize_once()
- self.resized_once = True
- self.current_channel_id = channel_id
+ if channel_id:
+ headers = {"Authorization": f"{self.token}"}
+ response = requests.get(f"https://discord.com/api/v9/channels/{channel_id}/messages", headers=headers, params={"limit": 20})
+ if response.status_code == 200:
+ messages_data = response.json()
+ self.display_messages(messages_data)
else:
QMessageBox.warning(self, "Error", "Failed to fetch messages.")
- def resize_once(self):
- new_width = self.calculate_new_width()
- self.resize(new_width, self.height())
-
- def calculate_new_width(self):
- content_width = self.messages_text_edit.sizeHint().width() + 30
- return max(self.width(), content_width)
-
- def fetch_messages(self, channel_id):
- headers = {"Authorization": f"{self.token}"}
- response = requests.get(f"https://discord.com/api/v9/channels/{channel_id}/messages", headers=headers, params={"limit": 5})
- if response.status_code == 200:
- messages_data = response.json()
- return messages_data
- else:
- return None
-
def display_messages(self, messages):
self.messages_text_edit.clear()
for message in reversed(messages):
@@ -349,116 +183,47 @@ def display_messages(self, messages):
self.messages_text_edit.append(f"{author}: {content}")
def send_message(self):
- if self.current_channel_id:
- headers = {"Authorization": f"{self.token}", "Content-Type": "application/json"}
- payload = {"content": self.message_input.text()}
- response = requests.post(f"https://discord.com/api/v9/channels/{self.current_channel_id}/messages", headers=headers, json=payload)
- if response.status_code == 200:
- self.message_input.clear()
- self.refresh_dm()
-
- def search_friends(self, text):
- for i in range(self.friends_list.count()):
- item = self.friends_list.item(i)
- if text.lower() in item.text().lower():
- item.setHidden(False)
- else:
- item.setHidden(True)
-
- def search_servers(self, text):
- for i in range(self.servers_list.count()):
- item = self.servers_list.item(i)
- if text.lower() in item.text().lower():
- item.setHidden(False)
- else:
- item.setHidden(True)
-
- def select_font(self):
- font, ok = QFontDialog.getFont()
- if ok:
- self.messages_text_edit.setFont(font)
-
- def open_emoji_dialog(self):
- dialog = EmojiDialog(self.emojis, self.token, self)
- dialog.exec_()
-
- def load_emojis(self, token):
- self.emojis = self.fetch_emojis(token)
-
- def fetch_emojis(self, token):
- headers = {"Authorization": f"{token}"}
- response = requests.get("https://discord.com/api/v9/users/@me", headers=headers)
- emojis = []
- if response.status_code == 200:
- user_data = response.json()
- emoji_response = requests.get(f"https://discord.com/api/v9/guilds/{user_data['id']}/emojis", headers=headers)
- if emoji_response.status_code == 200:
- emojis_data = emoji_response.json()
- for emoji in emojis_data:
- emojis.append(emoji['name'])
- return emojis
-
-
-class LoginScreen(QWidget):
- login_signal = pyqtSignal(str)
-
- def __init__(self):
- super().__init__()
- self.setWindowTitle("Login")
- self.layout = QVBoxLayout()
-
- self.token_input = QLineEdit()
- self.token_input.setPlaceholderText("Enter your token...")
- self.layout.addWidget(self.token_input)
-
- self.login_button = QPushButton("Login")
- self.login_button.clicked.connect(self.emit_login_signal)
- self.layout.addWidget(self.login_button)
-
- self.setLayout(self.layout)
-
- def emit_login_signal(self):
- token = self.token_input.text().strip()
- if token:
- self.login_signal.emit(token)
-
-
-class EmojiDialog(QDialog):
- def __init__(self, emojis, token, parent=None):
- super().__init__(parent)
- self.setWindowTitle("Select Emoji")
- self.layout = QGridLayout()
-
- self.scroll_area = QScrollArea()
- self.scroll_area.setWidgetResizable(True)
- self.scroll_widget = QWidget()
- self.scroll_layout = QGridLayout(self.scroll_widget)
-
- row = 0
- col = 0
- for emoji in emojis:
- button = QPushButton(emoji)
- button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
- button.clicked.connect(lambda checked, name=emoji: self.insert_emoji(name))
- self.scroll_layout.addWidget(button, row, col)
- col += 1
- if col == 5:
- col = 0
- row += 1
- if row == 5:
- break
-
- self.scroll_area.setWidget(self.scroll_widget)
- self.layout.addWidget(self.scroll_area)
-
- self.setLayout(self.layout)
-
- def insert_emoji(self, name):
- self.parent().message_input.insert(f"<:{name}>")
-
+ message = self.message_input.text()
+ selected_tab_index = self.tabs.currentIndex()
+
+ if selected_tab_index == 0: # If Friends tab is selected
+ selected_item = self.friends_list.currentItem()
+ if selected_item:
+ recipient_id = selected_item.data(Qt.UserRole)
+ self.send_direct_message(recipient_id, message)
+ elif selected_tab_index == 1: # If Servers tab is selected
+ selected_item = self.servers_list.currentItem()
+ if selected_item:
+ channel_id = selected_item.data(Qt.UserRole)
+ self.send_channel_message(channel_id, message)
+
+ def send_direct_message(self, recipient_id, message):
+ headers = {"Authorization": f"{self.token}", "Content-Type": "application/json"}
+ payload = {"content": message}
+ response = requests.post(f"https://discord.com/api/v9/channels/{recipient_id}/messages", headers=headers, json=payload)
+ if response.status_code != 200:
+ QMessageBox.warning(self, "Error", "Failed to send message.")
+
+ def send_channel_message(self, channel_id, message):
+ headers = {"Authorization": f"{self.token}", "Content-Type": "application/json"}
+ payload = {"content": message}
+ response = requests.post(f"https://discord.com/api/v9/channels/{channel_id}/messages", headers=headers, json=payload)
+ if response.status_code != 200:
+ QMessageBox.warning(self, "Error", "Failed to send message.")
+
+ def refresh_messages(self):
+ selected_tab_index = self.tabs.currentIndex()
+ if selected_tab_index == 0: # If Friends tab is selected
+ selected_item = self.friends_list.currentItem()
+ if selected_item:
+ self.load_channel_messages(selected_item)
+ elif selected_tab_index == 1: # If Servers tab is selected
+ selected_item = self.servers_list.currentItem()
+ if selected_item:
+ self.load_channel_messages(selected_item)
if __name__ == "__main__":
app = QApplication(sys.argv)
- naticord = Naticord()
- naticord.show()
+ client = Naticord()
+ client.show()
sys.exit(app.exec_())
diff --git a/settings.ini b/settings.ini
deleted file mode 100644
index 8d99aae..0000000
--- a/settings.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[Settings]
-mode = dark
-
diff --git a/settings.svg b/settings.svg
deleted file mode 100644
index e45b0b6..0000000
--- a/settings.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/settings_white.svg b/settings_white.svg
deleted file mode 100644
index 471db7a..0000000
--- a/settings_white.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-