2 Commits

Author SHA1 Message Date
fb520357e9 group fixes 2022-10-08 03:22:09 +00:00
be6eb0e2a9 fixes 2022-10-08 02:46:23 +00:00
20 changed files with 216 additions and 71 deletions

1
.gitignore vendored
View File

@@ -26,3 +26,4 @@ Toxygen.egg-info
.cache
*.db
.pylint.sh

25
ToDo.md
View File

@@ -16,24 +16,29 @@ The code is in there but it's not working.
I may have broken it trying to wire up the ability to
set the audio device from the command line.
## Group peer_id
## Groups
There has been a change of API on a field named group.peer_id
The code is broken in places because I have not seen the path
to change from the old API ro the new one.
1. peer_id There has been a change of API on a field named
```group.peer_id``` The code is broken in places because I have not
seen the path to change from the old API ro the new one.
2. There is no way to delete a group in the UI
3. Distinguish between Frieds, Groups and Whispers in the UI.
## Plugin system
Needs better documentation and checking.
There's something broken in the way some of them plug into Qt menus.
1. Needs better documentation and checking.
Should the plugins be in toxygen or a separate repo?
2. There's something broken in the way some of them plug into Qt menus.
3. Should the plugins be in toxygen or a separate repo?
## check toxygen_wrapper
I've broken out toxygen_wrapper to be standalone,
https://git.plastiras.org/emdee/toxygen_wrapper
but the tox.py needs each call double checking.
1. I've broken out toxygen_wrapper to be standalone,
https://git.plastiras.org/emdee/toxygen_wrapper but the tox.py
needs each call double checking.

View File

@@ -38,7 +38,7 @@ def setup_logging(oArgs):
setattr(oArgs, 'log_oFd', oFd)
aKw['stream'] = oFd
coloredlogs.install(**aKw)
else:
aKw = dict(level=oArgs.loglevel,
format='%(name)s %(levelname)-4s %(message)s')
@@ -162,6 +162,7 @@ class App:
LOG.info("Starting toxygen version " +version)
self._version = version
self._tox = None
self._app = self._settings = self._profile_manager = None
self._plugin_loader = self._messenger = None
self._tox = self._ms = self._init = self._main_loop = self._av_loop = None
@@ -268,7 +269,7 @@ class App:
if hasattr(oArgs, 'log_oFd'):
oArgs.log_oFd.close()
delattr(oArgs, 'log_oFd')
# failsafe: segfaults on exit
if hasattr(self, '_tox'):
if self._tox and hasattr(self._tox, 'kill'):
@@ -777,6 +778,7 @@ class App:
retval = tox_factory(data=data, settings=settings_,
args=self._args, app=self)
LOG.debug("_create_tox succeeded")
self._tox = retval
return retval
def _force_exit(self, retval=0):
@@ -925,7 +927,7 @@ class App:
shuffle(lElts)
LOG.debug(f"_test_bootstrap #Elts={len(lElts)}")
LOG.trace(f"_test_bootstrap lElts={lElts[:8]}")
shuffle(env['lElts'])
shuffle(env['lElts'])
for host,port,key in lElts[:8]:
try:
assert len(key) == 64, key

View File

@@ -101,7 +101,7 @@ class AV(common.tox_save.ToxAvSave):
def accept_call(self, friend_number, audio_enabled, video_enabled):
# obsolete
return call_accept_call(self, friend_number, audio_enabled, video_enabled)
def call_accept_call(self, friend_number, audio_enabled, video_enabled):
LOG.debug(f"call_accept_call from {friend_number} {self._running}" +
f"{audio_enabled} {video_enabled}")
@@ -304,7 +304,7 @@ class AV(common.tox_save.ToxAvSave):
LOG.info("start_video_thread " \
+f" device: {s['video']['device']}" \
+f" supported: {s['video']['width']} {s['video']['height']}")
self._video_running = True
self._video_thread = BaseThread(target=self.send_video,
name='_video_thread')

View File

@@ -114,7 +114,7 @@ class CallsManager:
util_ui.tr('ERROR Accepting call from {friend_number}'))
else:
self._main_screen.active_call()
finally:
# does not terminate call - just the av_widget
if friend_number in self._incoming_calls:
@@ -127,7 +127,7 @@ class CallsManager:
pass
LOG.debug(f" closed self._call_widgets[{friend_number}]")
def stop_call(self, friend_number, by_friend):
"""
Stop call with friend

View File

@@ -44,7 +44,7 @@ class ContactsManager(ToxSave):
try:
self._ms._log(s)
except: pass
def get_contact(self, num):
if num < 0 or num >= len(self._contacts):
return None
@@ -254,10 +254,14 @@ class ContactsManager(ToxSave):
group = self.get_group_by_number(group_number)
peer = group.get_peer_by_id(peer_id)
if peer: # broken
if not self.check_if_contact_exists(peer.public_key):
self.add_group_peer(group, peer)
return self.get_contact_by_tox_id(peer.public_key)
if hasattr(peer, 'public_key'):
LOG.error(f'no peer public_key ' + repr(dir(peer)))
else:
if not self.check_if_contact_exists(peer.public_key):
self.add_group_peer(group, peer)
return self.get_contact_by_tox_id(peer.public_key)
else:
LOG.warn(f'no peer group_number={group_number}')
def check_if_contact_exists(self, tox_id):
return any(filter(lambda c: c.tox_id == tox_id, self._contacts))
@@ -455,7 +459,7 @@ class ContactsManager(ToxSave):
title = 'Friend failed'
text = 'Friend failed sending friend request'
retval = text
except Exception as ex: # wrong data
title = 'Friend add exception'
text = 'Friend request exception with ' + str(ex)
@@ -466,7 +470,7 @@ class ContactsManager(ToxSave):
text = util_ui.tr(text)
util_ui.message_box(text, title)
return retval
def process_friend_request(self, tox_id, message):
"""
Accept or ignore friend request

View File

@@ -86,7 +86,7 @@ class GroupChat(contact.Contact, ToxSave):
if peer_id > self._peers_limit:
LOG_WARN(f"add_peer id={peer_id} > {self._peers_limit}")
return
peer = GroupChatPeer(peer_id,
self._tox.group_peer_get_name(self._number, peer_id),
self._tox.group_peer_get_status(self._number, peer_id),

View File

@@ -7,6 +7,9 @@ from groups.group_invite import GroupInvite
import wrapper.toxcore_enums_and_consts as constants
from wrapper.toxcore_enums_and_consts import *
global LOG
import logging
LOG = logging.getLogger('app.'+'gs')
class GroupsService(tox_save.ToxSave):
@@ -19,6 +22,8 @@ class GroupsService(tox_save.ToxSave):
self._widgets_factory_provider = widgets_factory_provider
self._group_invites = []
self._screen = None
# maybe just use self
self._tox = tox
def set_tox(self, tox):
super().set_tox(tox)
@@ -30,7 +35,11 @@ class GroupsService(tox_save.ToxSave):
# -----------------------------------------------------------------------------------------------------------------
def create_new_gc(self, name, privacy_state, nick, status):
group_number = self._tox.group_new(privacy_state, name, nick, status)
try:
group_number = self._tox.group_new(privacy_state, name, nick, status)
except Exception as e:
LOG.error(f"create_new_gc {e}")
return
if group_number == -1:
return
@@ -48,8 +57,9 @@ class GroupsService(tox_save.ToxSave):
# -----------------------------------------------------------------------------------------------------------------
def leave_group(self, group_number):
self._tox.group_leave(group_number)
self._contacts_manager.delete_group(group_number)
if type(group_number) == int:
self._tox.group_leave(group_number)
self._contacts_manager.delete_group(group_number)
def disconnect_from_group(self, group_number):
self._tox.group_disconnect(group_number)
@@ -73,7 +83,7 @@ class GroupsService(tox_save.ToxSave):
e = f"Friend not connected friend_number={friend_number}"
util_ui.message_box(title +'\n' +str(e), title)
return
try:
self._tox.group_invite_friend(group_number, friend_number)
except Exception as e:
@@ -246,8 +256,14 @@ class GroupsService(tox_save.ToxSave):
self._group_invites.remove(invite)
def _join_gc_via_invite(self, invite_data, friend_number, nick, status, password):
group_number = self._tox.group_invite_accept(invite_data, friend_number, nick, status, password)
self._add_new_group_by_number(group_number)
if nick is None: nick = ''
if invite_data is None: invite_data = ''
try:
group_number = self._tox.group_invite_accept(invite_data, friend_number, nick, status, password)
except Exception as e:
LOG.error(f"_join_gc_via_invite {e}")
else:
self._add_new_group_by_number(group_number)
def _update_invites_button_state(self):
self._main_screen.update_gc_invites_button_state()

View File

@@ -306,13 +306,13 @@ lKEEP_SETTINGS = ['uri',
'loglevel',
'logfile',
'mode',
# dunno
'audio_input',
'audio_output',
'audio',
'video',
'ipv6_enabled',
'udp_enabled',
'local_discovery_enabled',
@@ -378,15 +378,16 @@ def main(lArgs):
#setattr(aArgs, 'video', setup_video(oArgs))
aArgs.video = setup_video(oArgs)
assert 'video' in aArgs.__dict__
#setattr(aArgs, 'audio', setup_audio(oArgs))
aArgs.audio = setup_audio(oArgs)
assert 'audio' in aArgs.__dict__
oArgs = aArgs
toxygen = app.App(__version__, oArgs)
global oAPP
oAPP = toxygen
__builtins__['app'] = toxygen
i = toxygen.iMain()
return i

View File

@@ -167,7 +167,7 @@ def friend_status_message(contacts_manager, messenger):
friend = contacts_manager.get_friend_by_number(friend_number)
key = f"friend_number={friend_number}"
if bTooSoon(key, sSlot, 10): return
invoke_in_main_thread(friend.set_status_message, str(status_message, 'utf-8'))
LOG_DEBUG(f'User #{friend_number} has new status message')
invoke_in_main_thread(messenger.send_messages, friend_number)
@@ -480,7 +480,7 @@ def group_private_message(window, tray, tox, messenger, settings, profile):
if settings['sound_notifications'] and bl and profile.status != TOX_USER_STATUS['BUSY']:
sound_notification(SOUND_NOTIFICATION['MESSAGE'])
icon = util.join_path(util.get_images_directory(), 'icon_new_messages.png')
if tray and hasattr(tray, 'setIcon'):
if tray and hasattr(tray, 'setIcon'):
invoke_in_main_thread(tray.setIcon, QtGui.QIcon(icon))
return wrapped

View File

@@ -119,11 +119,11 @@ class InitThread(BaseThread):
args=list(),
kwargs=dict(lElts=None, oThread=self, iMax=2)
).start()
if self._is_first_start:
LOG_INFO('starting plugins')
self._plugin_loader.load()
except Exception as e:
LOG_DEBUG(f"InitThread run: ERROR {e}")
pass
@@ -157,7 +157,7 @@ class ToxAVIterateThread(BaseQThread):
def __init__(self, toxav):
super().__init__()
self._toxav = toxav
def run(self):
LOG_DEBUG('ToxAVIterateThread run: ')
while not self._stop_thread:

View File

@@ -50,7 +50,7 @@ class GroupBansScreen(CenteredWidget):
def _retranslate_ui(self):
# self.setWindowTitle(util_ui.tr('Bans list for group "{}"').format(self._group.name))
pass
def _refresh_bans_list(self):
self.bansListWidget.clear()
can_cancel_ban = self._group.is_self_moderator_or_founder()

View File

@@ -27,6 +27,7 @@ class GroupInvitesScreen(CenteredWidget):
self._groups_service = groups_service
self._profile = profile
self._contacts_provider = contacts_provider
self._tox = self._groups_service._tox
uic.loadUi(util.get_views_path('group_invites_screen'), self)
@@ -68,6 +69,8 @@ class GroupInvitesScreen(CenteredWidget):
password = self.passwordLineEdit.text()
status = self.statusComboBox.currentIndex()
if not nick:
nick = self._tox.self_get_name()
selected_invites = self._get_selected_invites()
for invite in selected_invites:
self._groups_service.accept_group_invite(invite, nick, status, password)
@@ -90,7 +93,7 @@ class GroupInvitesScreen(CenteredWidget):
for index in range(items_count):
list_item = self.invitesListWidget.item(index)
item_widget = self.invitesListWidget.itemWidget(list_item)
if item_widget.is_selected():
if item_widget and item_widget.is_selected():
selected.append(all_invites[index])
return selected

View File

@@ -3,7 +3,6 @@ from PyQt5 import uic
import utils.util as util
import utils.ui as util_ui
class GroupManagementScreen(CenteredWidget):
def __init__(self, groups_service, group):
@@ -21,6 +20,7 @@ class GroupManagementScreen(CenteredWidget):
self.privacyStateComboBox.setCurrentIndex(1 if self._group.is_private else 0)
self.peersLimitSpinBox.setValue(self._group.peers_limit)
self.deletePushButton.clicked.connect(self._delete)
self.savePushButton.clicked.connect(self._save)
def _retranslate_ui(self):
@@ -28,12 +28,21 @@ class GroupManagementScreen(CenteredWidget):
self.passwordLabel.setText(util_ui.tr('Password:'))
self.peerLimitLabel.setText(util_ui.tr('Peer limit:'))
self.privacyStateLabel.setText(util_ui.tr('Privacy state:'))
self.deletePushButton.setText(util_ui.tr('Delete'))
self.savePushButton.setText(util_ui.tr('Save'))
self.privacyStateComboBox.clear()
self.privacyStateComboBox.addItem(util_ui.tr('Public'))
self.privacyStateComboBox.addItem(util_ui.tr('Private'))
def _delete(self):
self._groups_service.leave_group(self._group.number)
self.close()
def _disconnect(self):
self._groups_service.disconnect_from_group(self._group.number)
self.close()
def _save(self):
password = self.passwordLineEdit.text()
privacy_state = self.privacyStateComboBox.currentIndex()

View File

@@ -76,13 +76,16 @@ class MainWindow(QtWidgets.QMainWindow):
self._contacts_manager = None
self._tray = tray
self._app = app
self._tox = app._tox
self._widget_factory = None
self._modal_window = None
self._plugins_loader = None
self.setAcceptDrops(True)
self._saved = False
self._smiley_window = None
self._profile = self._toxes = self._messenger = None
self._profile = None
self._toxes = None
self._messenger = None
self._file_transfer_handler = self._history_loader = self._groups_service = self._calls_manager = None
self._should_show_group_peers_list = False
self.initUI()
@@ -91,6 +94,7 @@ class MainWindow(QtWidgets.QMainWindow):
# take a rough guess of 2/3 the default width at the default font
iMAX = settings['width'] * 2/3 / settings['message_font_size']
self._me = LogDialog(self, app)
self._pe = None
def set_dependencies(self, widget_factory, tray, contacts_manager, messenger, profile, plugins_loader,
file_transfer_handler, history_loader, calls_manager, groups_service, toxes, app):
@@ -159,6 +163,8 @@ class MainWindow(QtWidgets.QMainWindow):
self.actionLog_console = QtWidgets.QAction(window)
self.actionLog_console.setObjectName("actionLog_console")
self.actionPython_console = QtWidgets.QAction(window)
self.actionPython_console.setObjectName("actionLog_console")
self.updateSettings = QtWidgets.QAction(window)
self.actionSettings = QtWidgets.QAction(window)
self.actionSettings.setObjectName("actionSettings")
@@ -196,6 +202,7 @@ class MainWindow(QtWidgets.QMainWindow):
self.menuPlugins.addAction(self.reloadPlugins)
self.menuPlugins.addAction(self.reloadToxchat)
self.menuPlugins.addAction(self.actionLog_console)
self.menuPlugins.addAction(self.actionPython_console)
self.menuAbout.addAction(self.actionAbout_program)
@@ -211,6 +218,7 @@ class MainWindow(QtWidgets.QMainWindow):
self.actionQuit_program.triggered.connect(self.quit_program)
self.actionAbout_program.triggered.connect(self.about_program)
self.actionLog_console.triggered.connect(self.log_console)
self.actionPython_console.triggered.connect(self.python_console)
self.actionNetwork.triggered.connect(self.network_settings)
self.actionAdd_friend.triggered.connect(self.add_contact_triggered)
self.createGC.triggered.connect(self.create_gc)
@@ -264,6 +272,7 @@ class MainWindow(QtWidgets.QMainWindow):
self.actionNetwork.setText(util_ui.tr("Network"))
self.actionAbout_program.setText(util_ui.tr("About program"))
self.actionLog_console.setText(util_ui.tr("Console Log"))
self.actionPython_console.setText(util_ui.tr("Python Console"))
self.actionTest_tox.setText(util_ui.tr("Bootstrap"))
self.actionTest_socks.setText(util_ui.tr("Test program"))
self.actionQuit_program.setText(util_ui.tr("Quit program"))
@@ -554,6 +563,18 @@ class MainWindow(QtWidgets.QMainWindow):
def log_console(self):
self._me.show()
def python_console(self):
try:
if not self._pe:
from pyqtconsole.console import PythonConsole
self._pe = PythonConsole(sFont="Courier New", bBold=True)
self._pe.show()
self._pe.eval_queued()
# self._pe.eval_in_thread()
except Exception as e:
LOG.debug(e)
self._me.show()
def about_program(self):
# TODO: replace with window
text = util_ui.tr('Toxygen is Tox client written in Python.\nVersion: ')

View File

@@ -43,7 +43,7 @@ class AddContact(CenteredWidget):
if self._bootstrap:
return
self._bootstrap = True
def _add_friend(self):
if self._adding:
return
@@ -497,7 +497,7 @@ class AudioSettings(CenteredWidget):
uic.loadUi(get_views_path('audio_settings_screen'), self)
self._update_ui()
self.center()
def closeEvent(self, event):
if 'audio' not in self._settings:
ex = f"self._settings=id(self._settings) {self._settings!r}"
@@ -618,7 +618,7 @@ class VideoSettings(CenteredWidget):
if 'device' not in self._settings['video']:
LOG.warn(f"'device' not in self._settings['video']: {self._settings!r}")
self._settings['video']['device'] = self._devices[-1]
self._settings['video']['device'] = self._devices[-1]
iIndex = self._settings['video']['device']
try:
index = self._devices.index(iIndex)
@@ -628,7 +628,7 @@ class VideoSettings(CenteredWidget):
se = f"Video devices index error: index={iIndex} {e}"
LOG.warn(se)
# util_ui.message_box(se, util_ui.tr(f"ERROR: Video devices error"))
self._settings['video']['device'] = self._devices[-1]
self._settings['video']['device'] = self._devices[-1]
self._retranslate_ui()

View File

@@ -412,4 +412,4 @@ class Settings(dict):
if key in aArgs.__dict__ and info[key] != val:
aRet[key] = val
return aRet

View File

@@ -3,7 +3,7 @@ import os
import sys
from ctypes import CDLL
# You need a libs directory beside this directory
# You need a libs directory beside this directory
# and you need to link your libtoxcore.so and libtoxav.so
# and libtoxencryptsave.so into ../libs/
# Link all 3 to libtoxcore.so if you have only libtoxcore.so
@@ -13,7 +13,7 @@ try:
except ImportError:
sLIBS_DIR = os.path.join(os.path.dirname(os.path.dirname(__file__)),
'libs')
class LibToxCore:
def __init__(self):
@@ -40,11 +40,11 @@ class LibToxCore:
class LibToxAV:
def __init__(self):
platform = util.get_platform()
if platform == 'Windows':
platform = sys.platform
if platform == 'win32':
# on Windows av api is in libtox.dll
self._libtoxav = CDLL(os.path.join(sLIBS_DIR, 'libtox.dll'))
elif platform == 'Darwin':
elif platform == 'darwin':
self._libtoxav = CDLL('libtoxcore.dylib')
else:
libFile = os.path.join(sLIBS_DIR, 'libtoxav.so')

View File

@@ -0,0 +1,80 @@
tox_version_major
tox_version_minor
tox_version_patch
tox_version_is_compatible
tox_public_key_size
tox_secret_key_size
tox_conference_uid_size
tox_conference_id_size
tox_nospam_size
tox_address_size
tox_max_name_length
tox_max_status_message_length
tox_max_friend_request_length
tox_max_message_length
tox_max_custom_packet_size
tox_hash_length
tox_file_id_length
tox_max_filename_length
tox_max_hostname_length
tox_options_get_ipv6_enabled
tox_options_get_udp_enabled
tox_options_get_local_discovery_enabled
tox_options_get_dht_announcements_enabled
tox_options_get_proxy_type
tox_options_get_proxy_port
tox_options_get_start_port
tox_options_get_end_port
tox_options_get_tcp_port
tox_options_get_hole_punching_enabled
tox_options_get_savedata_type
tox_options_get_savedata_length
tox_options_get_experimental_thread_safety
tox_file_seek
tox_callback_conference_connected
tox_callback_conference_message
tox_callback_conference_title
tox_callback_conference_peer_list_changed
tox_conference_new
tox_conference_delete
tox_conference_peer_count
tox_conference_peer_get_name_size
tox_conference_peer_get_name
tox_conference_peer_get_public_key
tox_conference_peer_number_is_ours
tox_conference_offline_peer_count
tox_conference_offline_peer_get_name_size
tox_conference_offline_peer_get_name
tox_conference_offline_peer_get_public_key
tox_conference_offline_peer_get_last_active
tox_conference_set_max_offline
tox_conference_invite
tox_conference_join
tox_conference_send_message
tox_conference_get_title_size
tox_conference_get_title
tox_conference_set_title
tox_conference_get_chatlist_size
tox_conference_get_chatlist
tox_conference_get_type
tox_conference_get_id
tox_conference_by_id
tox_conference_get_uid
tox_conference_by_uid
tox_group_max_topic_length
tox_group_max_part_length
tox_group_max_group_name_length
tox_group_max_password_size
tox_group_chat_id_size
tox_group_peer_public_key_size
tox_group_peer_get_connection_status
tox_group_get_voice_state
tox_callback_group_voice_state
tox_group_get_topic_lock
tox_callback_group_topic_lock
tox_group_send_custom_private_packet
tox_callback_group_custom_private_packet
tox_group_founder_set_topic_lock
tox_group_founder_set_voice_state
tox_group_set_ignore
tox_group_mod_kick_peer

View File

@@ -172,7 +172,7 @@ class Tox:
LOG_ERROR(f"tox_kill {e!s}")
else:
LOG_DEBUG(f"tox_kill")
# -----------------------------------------------------------------------------------------------------------------
# Startup options
# -----------------------------------------------------------------------------------------------------------------
@@ -287,7 +287,7 @@ class Tox:
LOG_ERROR(f"libtoxcore.tox_bootstrap {e}")
# dunno
raise
tox_err_bootstrap = tox_err_bootstrap.value
if tox_err_bootstrap == TOX_ERR_BOOTSTRAP['OK']:
return bool(result)
@@ -776,7 +776,7 @@ class Tox:
if friend_list is None:
friend_list = create_string_buffer(sizeof(c_uint32) * friend_list_size)
friend_list = POINTER(c_uint32)(friend_list)
LOG_DEBUG(f"tox_self_get_friend_list")
LOG_TRACE(f"tox_self_get_friend_list")
Tox.libtoxcore.tox_self_get_friend_list(self._tox_pointer, friend_list)
return friend_list[0:friend_list_size]
@@ -1341,7 +1341,7 @@ class Tox:
POINTER(None)())
self.file_recv_control_cb = None
return
LOG_DEBUG(f"tox_callback_file_recv_control")
c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_int, c_void_p)
self.file_recv_control_cb = c_callback(callback)
@@ -1607,7 +1607,7 @@ class Tox:
POINTER(None)())
self.file_recv_cb = None
return
LOG_DEBUG(f"tox_callback_file_recv")
c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_uint32, c_uint64, c_char_p, c_size_t, c_void_p)
self.file_recv_cb = c_callback(callback)
@@ -1640,7 +1640,7 @@ class Tox:
POINTER(None)())
self.file_recv_chunk_cb = None
return
LOG_DEBUG(f"tox_callback_file_recv_chunk")
c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_uint64, POINTER(c_uint8), c_size_t, c_void_p)
self.file_recv_chunk_cb = c_callback(callback)
@@ -1764,7 +1764,7 @@ class Tox:
self.friend_lossless_packet_cb = None
self.libtoxcore.tox_callback_friend_lossless_packet(self._tox_pointer, POINTER(None)())
return
LOG_DEBUG(f"callback_friend_lossless_packet")
c_callback = CFUNCTYPE(None, c_void_p, c_uint32, POINTER(c_uint8), c_size_t, c_void_p)
self.friend_lossless_packet_cb = c_callback(callback)
@@ -2387,7 +2387,6 @@ class Tox:
error = c_int()
buff = create_string_buffer(TOX_GROUP_CHAT_ID_SIZE)
LOG_DEBUG(f"tox_group_get_chat_id")
result = Tox.libtoxcore.tox_group_get_chat_id(self._tox_pointer,
group_number,
buff, byref(error))
@@ -2575,23 +2574,27 @@ class Tox:
# -----------------------------------------------------------------------------------------------------------------
def group_send_custom_packet(self, group_number, lossless, data):
"""
Send a custom packet to the group.
"""Send a custom packet to the group.
If lossless is true the packet will be lossless. Lossless packet behaviour is comparable
to TCP (reliability, arrive in order) but with packets instead of a stream.
If lossless is true the packet will be lossless. Lossless
packet behaviour is comparable to TCP (reliability, arrive in
order) but with packets instead of a stream.
If lossless is false, the packet will be lossy. Lossy packets behave like UDP packets,
meaning they might never reach the other side or might arrive more than once (if someone
is messing with the connection) or might arrive in the wrong order.
If lossless is false, the packet will be lossy. Lossy packets
behave like UDP packets, meaning they might never reach the
other side or might arrive more than once (if someone is
messing with the connection) or might arrive in the wrong
order.
Unless latency is an issue or message reliability is not important, it is recommended that you use
lossless custom packets.
Unless latency is an issue or message reliability is not
important, it is recommended that you use lossless custom
packets.
:param group_number: The group number of the group the message is intended for.
:param lossless: True if the packet should be lossless.
:param data A byte array containing the packet data.
:return True on success.
"""
error = c_int()