Compare commits

..
Author SHA1 Message Date
deepend 1d24941ed5 fix timestamp selection handling 2026-10-25 04:46:53 -06:00
deepend-tildeclub 6e5ed9270e Merge pull request #380 from ZoiteChat/implement-local-docs
Implement locally stored docs. Uses web when local not available.
2026-10-24 17:35:19 -06:00
deepend 5805720416 remove basicstap 2026-10-24 17:20:11 -06:00
deepend 572edbd634 add sphinx to appimage build 2026-10-24 17:20:11 -06:00
deepend 2a42f6cae0 prune unused local theme assets 2026-10-24 17:20:11 -06:00
deepend 8348afca8e Fix Windows build for offline docs include 2026-10-24 17:20:11 -06:00
deepend 23cb16e8b4 Implement locally stored docs. Uses web when local not available. 2026-10-24 17:20:10 -06:00
deepend 81f4f591f5 fix favicon 2026-10-24 13:14:22 -06:00
deepend-tildeclub 8e7885c7f6 Merge pull request #434 from ZoiteChat/fix-timestamps
Fix timestamps when toggling nick indentation
2026-10-22 04:29:54 -06:00
deepend 03c4e872c0 Fix timestamps when toggling nick indentation 2026-10-20 15:33:29 -06:00
deepend-tildeclub 9b65e5be73 Merge pull request #432 from ZoiteChat/fix-macos-keyboard-commands
macos: native editing shortcuts+resolve keybinding conflicts
2026-10-19 15:34:32 -06:00
deepend 9547dd3711 prioritize editing shortcuts 2026-10-19 15:25:28 -06:00
deepend-tildeclub abf49ce756 Merge pull request #431 from ZoiteChat/dependabot/github_actions/actions/setup-python-7
ci: bump actions/setup-python from 6 to 7
2026-10-19 13:16:45 -06:00
deepend-tildeclub 6bf2f34fca Merge pull request #430 from ZoiteChat/dependabot/github_actions/actions/checkout-7
ci: bump actions/checkout from 5 to 7
2026-10-19 13:16:23 -06:00
deepend-tildeclub 2ea74498ed Merge pull request #429 from ZoiteChat/dependabot/github_actions/actions/upload-artifact-7
ci: bump actions/upload-artifact from 6 to 7
2026-10-19 13:16:05 -06:00
deepend-tildeclub 7004c679f2 Merge pull request #425 from juergbi/jbilleter/keybindings
Fix menu offsets to fix key bindings
2026-10-19 13:15:47 -06:00
deepend-tildeclub c9de3f2ce0 Merge pull request #428 from ZoiteChat/dependabot/submodules/flatpak/shared-modules-a58e654
Bump flatpak/shared-modules from `55a86b3` to `a58e654`
2026-10-19 12:56:58 -06:00
dependabot[bot] f77d4f6d59 ci: bump actions/setup-python from 6 to 7
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 6 to 7.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-19 18:38:31 +00:00
dependabot[bot] 89b9a32168 ci: bump actions/checkout from 5 to 7
Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 7.
- [Release notes](https://github.com/actions/checkout/releases)
- [Commits](https://github.com/actions/checkout/compare/v5...v7)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-19 18:38:27 +00:00
dependabot[bot] 357b766369 ci: bump actions/upload-artifact from 6 to 7
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 6 to 7.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-19 18:38:23 +00:00
deepend 45a8ba18a6 expand Dependabot coverage and group updates 2026-10-19 12:36:50 -06:00
dependabot[bot] 0e0f8f72b2 Bump flatpak/shared-modules from 55a86b3 to a58e654
Bumps [flatpak/shared-modules](https://github.com/flathub/shared-modules) from `55a86b3` to `a58e654`.
- [Commits](https://github.com/flathub/shared-modules/compare/55a86b3da4969984f7de8fb02b3370247c9a266d...a58e654b9113feffaee113cc7167ebbbdb406bee)

---
updated-dependencies:
- dependency-name: flatpak/shared-modules
  dependency-version: a58e654b9113feffaee113cc7167ebbbdb406bee
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-19 18:36:21 +00:00
deepend dc5f8f7631 automate shared-modules updates 2026-10-19 12:21:11 -06:00
deepend-tildeclub 3a32b1eae6 Merge pull request #423 from ZoiteChat/macos-build
begin rebuild of macos build
2026-10-17 14:51:54 -06:00
deepend 7bf7e31459 embed the standard scaling rules 2026-10-17 13:08:33 -06:00
deepend c931867c4a visual check 2026-10-17 13:08:33 -06:00
deepend a2c0eb646f rebuild macos build 2026-10-17 13:08:32 -06:00
Jürg Billeter 8a03585611 Fix menu offsets to fix key bindings
Key bindings for `copy-selection`, `search-text` and others weren't
working due to incorrect offsets.
2026-10-16 22:14:35 +02:00
deepend-tildeclub 74d9e35b37 Merge pull request #422 from ZoiteChat/msi-msix
add msi/msix build support
2026-10-14 18:13:31 -07:00
deepend 1ae2d41378 fix menu.c build warnings 2026-10-14 05:32:13 -06:00
deepend-tildeclub 070603b0fe Merge pull request #421 from botovq/push-nsqmxvzmntnz
Fix clang -Wstrict-prototypes warnings
2026-10-13 18:16:48 -07:00
deepend 9e3daf494f msi/msix support 2026-10-13 19:13:50 -06:00
deepend-tildeclub 8f47b5cedf Merge pull request #418 from botovq/push-vzwkspvpmqvu
challengeauth_response (): move digest to stack
2026-10-13 18:13:15 -07:00
Theo Buehler 646ad029f0 Fix clang -Wstrict-prototypes warnings
This trivial diff gets rid of noisy warnings such as
```
./src/common/ignore.c:274:13: warning: a function declaration without a prototype is deprecated in all versions of C [-Wstrict-prototypes]
  274 | ignore_load ()
      |             ^
      |              void
```
when building with modern clang. This is cosmetic since these functions
are actually prototyped in a header, so if the header is in scope it's
not possible to call them with an arbitrary number of arguments.
2026-10-13 09:46:10 +02:00
Theo Buehler d47963247d challengeauth_response (): move digest to stack
Using HMAC ()'s facility of returning the digest in a static array is
not thread safe (this can't be fixed) and it is not portable to the
OpenSSL forks: BoringSSL removed support for this a decade ago (so
aws-lc doesn't support it either) and LibreSSL followed a few years
back.

There is no downside to having a 32-byte array on the stack and it would
make zoitechat safe to use from other forks without the need of patching
it.

Add an error check for the HMAC () call and handle the error in the
caller.
2026-10-13 09:25:22 +02:00
deepend 4f7add046c match theme better 2026-10-12 19:05:31 -06:00
deepend-tildeclub 61ec9af5e3 Merge pull request #420 from ZoiteChat/docs-theme-update
update theme to match new site
2026-10-12 17:11:27 -07:00
deepend 412dc92b10 update theme to match new site 2026-10-12 18:04:49 -06:00
deepend-tildeclub 04dab3dcd6 Merge pull request #412 from TehPeGaSuS/wrap-align-no-indent
Add option to align wrapped lines under the nick or message
2026-10-12 15:28:33 -07:00
ThePeGaSuS c51f885253 add option to align wrapped lines under the nick or message
When nick indentation is off, lines after the first wrap back to the
left edge, under the timestamp. Add a text_wrap_align preference
(start of line, prefix, message) so they can line up under the nick or
the message text instead. It is applied at wrap time, so existing
scrollback is re-wrapped when the setting changes, and the menu is
greyed out while nick indentation is on.
2026-10-12 17:43:09 +02:00
deepend-tildeclub aa7e20fee9 Merge pull request #417 from botovq/push-kwovwrnwwuzn
Minor simplification of _SSL_get_cert_info ()
2026-10-12 06:40:46 -07:00
deepend-tildeclub 480b99b1e7 Merge pull request #413 from B4reMetal/hex-color-parsing
feat(xtext): render \x04 hex color codes in chat text
2026-10-12 06:37:19 -07:00
BareMetal 0a4367b1da fix(xtext): move hexcolor_parse to its own file for the standalone xtext tests
master's new xtext_tests target compiles xtext.c on its own with only
GTK linked, so hexcolor_parse cannot live in util.c. Keep it in a
dependency-free hexcolor.c that both test targets compile, and register
it in the Visual Studio project for the Windows build.

Claude-Session: https://claude.ai/code/session_01Jxd4AknGzdAENADWAGHCzx
2026-10-11 23:58:09 -06:00
BareMetal 39daca9d22 feat(xtext): render \x04 hex color codes in chat text 2026-10-11 23:54:08 -06:00
deepend-tildeclub d614e25792 Merge pull request #419 from ZoiteChat/xtext-modernize
modernize rendering and fix scrollback resizing
2026-10-11 22:48:52 -07:00
deepend 30209d0c99 modernize rendering and fix scrollback resizing 2026-10-11 23:02:13 -06:00
Theo Buehler e1c44feb01 Minor simplification of _SSL_get_cert_info ()
Instead of retrieving the AlgorithmIdentifier and then reaching into it
to get the OID, the relevant OID can be obtained directly from the same
function.
2026-10-11 17:45:37 +02:00
deepend 5af8f4ede1 correct echo-message capability negotiation 2026-10-03 19:52:16 -06:00
deepend-tildeclub e11604fd62 Merge pull request #405 from ZoiteChat/fix-dialog-layout
make dialog content fill available space
2026-10-01 05:23:22 -07:00
deepend a2e8d9087e make dialog content fill available space 2026-09-30 16:22:47 -06:00
deepend-tildeclub edf268dc0f Merge pull request #404 from ZoiteChat/run-as-root
Block root unless --run-as-root
2026-09-30 10:23:51 -07:00
deepend e03ea59f3c Block root unless --run-as-root 2026-09-30 10:42:28 -06:00
deepend f0b2840668 ensure fixes added to main site are reflected on docs site 2026-09-29 15:33:41 -06:00
deepend-tildeclub 4f2fc49475 Merge pull request #402 from ZoiteChat/docs-theme-fixes
docs site theme consistency fixes
2026-09-29 08:51:46 -07:00
deepend 2e4155bbfc docs site theme consistency fixes 2026-09-29 09:50:22 -06:00
deepend-tildeclub ca2456cbc6 Merge pull request #401 from ZoiteChat/fix-ircv3-typing
Fix IRCv3 typing notifications
2026-09-26 05:52:36 -07:00
deepend 222b4868f5 Fix IRCv3 typing notifications 2026-09-26 06:10:20 -06:00
deepend-tildeclub 2cf1f7e833 Merge pull request #398 from ZoiteChat/fix-issue-361
restrict input redraws to the entry widget
2026-09-19 22:33:03 -07:00
deepend 776d24695e keep X11 selection ownership off the chat window 2026-09-18 05:29:03 -06:00
deepend-tildeclub 64a9c6e5d7 Merge pull request #397 from ZoiteChat/fix-issue-392
Fix Wayland text selection crash
2026-09-17 10:14:14 -07:00
deepend 66196013bc Fix Wayland text selection crash 2026-09-16 16:23:38 -06:00
deepend-tildeclub 61ff308c9f Merge pull request #395 from B4reMetal/fix-userlist-color-pref
fix(userlist): decouple userlist nick colors from text_color_nicks
2026-09-16 14:00:16 -07:00
deepend 2672f9b087 Start acknowleding 2.20 development 2026-09-16 01:16:35 -06:00
deepend 4315a75a12 add 2.19.1 release notes 2026-09-16 00:10:49 -06:00
BareMetal 15905bebb1 fix(userlist): decouple userlist nick colors from text_color_nicks 2026-09-15 19:38:34 -06:00
deepend-tildeclub fc4a7454ad Merge pull request #391 from ZoiteChat/fix-away-tracking
Fix away tracking
2026-09-15 11:45:16 -07:00
deepend 87410aea50 Fix away tracking 2026-09-15 12:30:04 -06:00
deepend-tildeclub de98d00a1c Merge pull request #390 from ZoiteChat/fix-issue-369
defer channel tree scrolling until realized
2026-09-14 05:36:00 -07:00
deepend e94ffd9d79 defer channel tree scrolling until realized 2026-09-14 04:11:23 -06:00
deepend-tildeclub d7aebb784d Merge pull request #387 from ZoiteChat/tab-font-custom
add custom tab fonts and reduce close button size
2026-09-14 02:56:19 -07:00
deepend-tildeclub 20b8cb939d Merge pull request #389 from ZoiteChat/fix-issue-386
fix theme service tests
2026-09-14 02:47:50 -07:00
deepend ff4217c3d1 fix theme service tests 2026-09-13 15:34:56 -06:00
deepend 4fb72d30d8 add custom tab fonts and reduce close button size 2026-09-12 06:43:08 -06:00
deepend-tildeclub d2b8fc0bda Merge pull request #352 from ZoiteChat/inline-images
support inline images in chat.
2026-09-11 02:51:36 -07:00
deepend 951181628a Bundle GIO TLS support in AppImage 2026-09-11 03:30:43 -06:00
deepend-tildeclub ccee274847 Merge pull request #384 from salahcoronya/build-improvements
Fix build bug and improve build process
2026-09-11 02:13:17 -07:00
deepend 967f6728ef Use OpenSSL for Win HTTPS image downloads 2026-09-11 02:58:53 -06:00
deepend-tildeclub bf11ffb626 Merge pull request #379 from ZoiteChat/fix-issue-373
Add formatting status bar to input box
2026-09-11 01:14:09 -07:00
deepend c12cabbe4e Add formatting status bar to input box 2026-09-10 22:40:11 -06:00
deepend-tildeclub e997594db9 Merge pull request #346 from ZoiteChat/emoji-major-updates
New Emoji Menu and Emoji Version 17.0 support.
2026-09-10 19:16:01 -07:00
deepend 6f5e3b395f fix failed CI builds 2026-09-10 19:55:33 -06:00
deepend 8bf2caf20d Unified emoji support across builds, fixed picker bugs, and added CI checks. 2026-09-10 07:13:41 -06:00
Christopher Byrne 040021ee0e buuld: Fix deprecated features since 0.56
Now the the minimum is 0.59, silence some warning in Meson:

dependency.get_pkgconfig_variable becomes
depenency.get_variable(pkgconfig: 'var_name')
executable(gui_app = true) becomes
executable(win_subsystem = 'windows')

Signed-off-by: Christopher Byrne <salah.coronya@gmail.com>
2026-09-07 20:57:29 -05:00
Christopher Byrne 5018a3a848 meson.build: Reflect usage of 0.59 Meson features
src/fe-gtk/meson.build calls feature_opt.allowed() which wasn't
added until 0.59.

Signed-off-by: Christopher Byrne <salah.coronya@gmail.com>
2026-09-07 20:57:29 -05:00
Christopher Byrne df1c74a91d data/misc/meson.build: Fix referencing possibly undefined variable "appstreamcli"
If meson is passed -Dinstall-plugin-metainfo=false, the "appstreamcli"
will not be defined, but it is later referenced in global scope, which
causes and "undefined variable" error in Meson

Signed-off-by: Christopher Byrne <salah.coronya@gmail.com>
2026-09-07 20:57:21 -05:00
deepend bb3893d36c Fix channel ACTION routing after self-message handling 2026-09-03 09:27:07 -06:00
deepend-tildeclub fe57449f6c Merge pull request #374 from TehPeGaSuS/fix/weechat-relay-pm-split
Fix private messages splitting into two windows over WeeChat's IRC relay
2026-09-03 08:20:55 -07:00
ThePeGaSuS c725adaa09 Fix CTCP ACTION not rerouting self-sent /me to correct PM window
Mirrors the PRIVMSG fix: replayed /me messages sent by our own nick
via WeeChat relay backlog were always passed with fromme=FALSE,
so they never got rerouted to the actual PM query window.
2026-09-03 08:51:21 +02:00
deepend-tildeclub 7731bfa4e0 Merge pull request #377 from ZoiteChat/retain-searched-text-view
unblock navigation when in search
2026-09-02 09:46:13 -07:00
deepend e72546aaec fix accidental removal of mg_apply_emoji_fallback_widget (entry); 2026-09-02 10:43:09 -06:00
deepend d6d648f0f4 unblock navigation when in search 2026-09-02 10:41:21 -06:00
deepend-tildeclub 56fddd56f5 Merge pull request #340 from bgermann/winspell
Require Enchant 2.8.19+ and drop win8 provider
2026-09-01 15:06:11 -07:00
Bastian Germann 3c9c350bc0 win32: Set Enchant prefix dir to install location
Enchant looks for its providers (including the WinSpell spellcheck
provider) under a hardcoded default path derived from its build
prefix, which is C:\gtk-build\gtk\x64\release\lib\enchant-2 when
built via gvsbuild. After installation, the provider DLLs live
under ZoiteChat's own install directory, so Enchant fails to find
them and spellcheck silently stops working.

Call enchant_set_prefix_dir() to redirect Enchant's provider search
to <install_dir> before the first enchant_broker_init().
enchant_set_prefix_dir() expects the installation prefix itself;
Enchant adds lib\enchant-2 when searching for providers.
The symbol is loaded optionally since it may not exist in older
Enchant builds; if missing, behavior is unchanged.
2026-09-01 21:36:40 +02:00
Bastian Germann 8340a04bd6 win32: Use parentheses to be independent of and/or evaluation order
The Version.NTPlatform shall be true for every evaluation of the if
branch.
2026-08-31 14:45:31 +02:00
Bastian Germann ccef70fd3d Clarify comments about spelling provider 2026-08-31 14:29:16 +02:00
Bastian Germann 0650fa7bbe win32: Reference enchant-2 in installer 2026-08-31 14:17:58 +02:00
Bastian Germann 5cffa06385 Update gvsbuild to version with updated enchant 2026-08-31 14:17:58 +02:00
Bastian Germann 4df17a151e Require Enchant 2.8.19+ and drop win8 provider
Require Enchant 2.8.19 or newer so Windows can use upstream winspell.
Remove the in-tree libenchant_win8 provider project and sources.
Update runtime library probing to prefer Enchant 2 library names.
2026-08-31 14:17:58 +02:00
deepend c8e314b690 docs version uses VERSION file 2026-08-30 23:39:07 -06:00
deepend 3d91f3c235 fix build issue for readthedocs.io 2026-08-30 23:19:20 -06:00
deepend 32cd536ada combine docs into zoitechat repo 2026-08-30 23:13:52 -06:00
ThePeGaSuS 0ba523d071 Fix: private messages split into two windows over WeeChat's IRC relay
When you send a private message, ZoiteChat sometimes gets the same
message echoed back by the server so it can show it in the right
window. Whether it recognizes that echo depended on the server first
confirming a feature called "echo-message" during connection setup.

WeeChat's relay doesn't seem to confirm that feature the way other
servers (or ZNC) do, so ZoiteChat didn't recognize its own echoed
messages and treated them as brand new incoming messages from
yourself, opening a second, separate conversation window instead of
using the one you were already typing in.

This makes ZoiteChat recognize its own message by checking who sent
it, instead of only trusting that feature confirmation. That way,
sent and received messages always end up in the same conversation
window, regardless of how well the server/relay announces its
capabilities.
2026-08-28 20:48:12 +02:00
deepend-tildeclub 314171b2b1 Merge pull request #367 from ZoiteChat/release/2.19
WIN32: fix installer theme download URL.
2026-08-16 01:38:36 -06:00
deepend 909309cc2e WIN32: fix installer theme download URL. 2026-08-15 12:49:58 -06:00
deepend-tildeclub 32b6de0c3a Merge pull request #366 from ZoiteChat/release/2.19-flatpak-error-startup
fix startup error on flatpak
2026-08-15 12:38:30 -06:00
deepend-tildeclub 7e22e7b67d Merge pull request #365 from ZoiteChat/release/2.19
Release/2.19
2026-08-15 12:37:35 -06:00
deepend 36f0d7450c fix startup error on flatpak 2026-08-15 12:20:18 -06:00
deepend-tildeclub 73d958911f Merge pull request #364 from ZoiteChat/master
run Ci for release branches
2026-08-15 12:10:29 -06:00
deepend 6b4a2b5406 fix unmark away option 2026-08-15 12:07:41 -06:00
deepend cd8d1c9b10 Run Ci on Release/version branches on commit 2026-08-04 12:11:30 -06:00
deepend-tildeclub a79fedda77 Merge pull request #360 from ZoiteChat/release/2.19
fix duplicate /msg output + Escaping of the intial slash
2026-08-04 12:10:53 -06:00
deepend 558ef7fbdf fix Escaping of the intial slash 2026-08-04 11:58:30 -06:00
deepend 7c6609a414 fix duplicate /msg output 2026-08-04 11:35:48 -06:00
deepend 54514f494f Version bump for 2.19.0 Release. 2026-07-28 10:26:04 -06:00
deepend-tildeclub 67aa7ca79f Merge pull request #355 from ZoiteChat/centralize_version_management
Centralize release version in root VERSION file.
2026-07-28 09:31:12 -06:00
deepend 344f5af01b Centralize release version in root VERSION file. 2026-07-28 09:12:53 -06:00
deepend-tildeclub 8150d6c48c Merge pull request #351 from ZoiteChat/reduce-client-restarts
Reduce needed client restarts for settings changes.
2026-07-19 17:02:04 -06:00
deepend 05ee0dbdb5 Reduce needed client restarts for settings changes. 2026-07-16 08:48:33 -06:00
deepend-tildeclub 2db3076c92 Merge pull request #350 from ZoiteChat/fix-gtk3-quirks
fix a few GTK3 theming quirks
2026-07-16 02:29:43 -06:00
deepend 90daed18df fix a few GTK3 theming quirks 2026-07-15 23:56:05 -06:00
deepend-tildeclub 65dd2c645a Merge pull request #345 from ZoiteChat/gtk3-consistency-fixes
theming: respect system theme by default
2026-07-14 14:27:08 -06:00
deepend 89d70a940c theming: respect system theme by default 2026-07-13 13:26:55 -06:00
deepend-tildeclub 4944517a0e Merge pull request #344 from ZoiteChat/ask_sasl_password_reconnect
Prompt SASL password again on reconnect
2026-07-13 09:53:41 -06:00
deepend 2a7ee7f0b2 Prompt SASL password again on reconnect 2026-07-13 09:18:19 -06:00
deepend-tildeclub 6edbd47c9a Merge pull request #342 from ZoiteChat/sasl_password_prompt
Add manual SASL password prompt
2026-07-10 05:12:19 -06:00
deepend d755c39e4a Add manual SASL password prompt 2026-07-09 12:50:13 -06:00
deepend-tildeclub b7f8327209 Merge pull request #341 from ZoiteChat/theme_manager_get_channel_tree_palette_behavior
Use owned channel tree font for palette behavior
2026-07-09 10:57:13 -06:00
deepend 27c82d8411 Use owned channel tree font for palette behavior 2026-07-09 10:38:52 -06:00
deepend e0b159c069 Remove Manjaro package. Handled seperately. 2026-07-08 12:24:08 -06:00
deepend-tildeclub cfb19820fa Merge pull request #330 from ZoiteChat/Windows/python38-support
Add Python 3.8 support for older windows versions.
2026-07-08 10:00:31 -06:00
deepend-tildeclub e05ca13608 Merge pull request #335 from ZoiteChat/color-tooltips
Add mouse over tooltips for each color selection.
2026-07-08 09:44:10 -06:00
deepend-tildeclub 15cf96c01f Merge pull request #336 from ZoiteChat/win32-taskbar-fix
Fix Windows taskbar click not minimizing the window
2026-07-08 09:31:04 -06:00
deepend 2caec91a0f Improve color tooltip design 2026-07-08 00:58:34 -06:00
deepend 9465a1e0a8 Fix Windows taskbar click not minimizing the window 2026-07-07 23:44:22 -06:00
deepend 251272c708 Applying: Fix Windows 7 crash after connecting when Python plugin init fails 2026-07-07 19:04:51 -06:00
deepend f068a0e0fa Fix Windows Python plugin DLL selection 2026-07-07 15:34:11 -06:00
deepend d0034cd112 Fixed missing comma in color tooltips 2026-07-07 14:14:36 -06:00
deepend ae6d5ebeef Add mouse over tooltips for each color selection. 2026-07-07 09:08:00 -06:00
deepend-tildeclub 8466e70424 Merge pull request #329 from ZoiteChat/implement-issue-326
Adjust default text-event colors
2026-07-06 23:46:48 -06:00
deepend c3d8f27f48 Add Python 3.8 support for older windows versions. 2026-07-06 01:37:59 -06:00
deepend b85229f1a1 Adjust default text-event colors 2026-07-05 23:54:49 -06:00
deepend-tildeclub d03298a048 Merge pull request #325 from ZoiteChat/fix-chat-scrollbar-stepper-scrolling
Fix chat scrollbar step increment for arrow scrolling
2026-07-05 15:07:25 -06:00
deepend-tildeclub 798e0b487e Merge pull request #324 from ZoiteChat/fix-nickserv-pass-auth
Fix NickServ auth to use decrypted active password
2026-07-05 13:50:48 -06:00
deepend 49444d72b4 Fix chat scrollbar step increment for arrow scrolling 2026-07-05 13:38:26 -06:00
deepend-tildeclub c0e0fe4dcb Merge pull request #319 from ZoiteChat/css-use-after-free-fix
Fix channel tree font ownership/lifetime
2026-07-04 20:07:58 -06:00
deepend-tildeclub 9a189848ea Merge pull request #318 from ZoiteChat/windows-browser-preference
Prefer ShellExecuteW for Windows URL opens
2026-07-04 20:07:37 -06:00
deepend 96b09bfc1d Fix NickServ auth to use decrypted active password 2026-07-04 20:04:11 -06:00
deepend 9a6c913d89 Fix channel tree font ownership/lifetime 2026-07-02 13:08:48 -06:00
deepend 42010594be Prefer ShellExecuteW for Windows URL opens 2026-07-02 13:02:09 -06:00
deepend 5469c30b75 Update GPLv2 text, Add feature request template. 2026-07-01 18:25:48 -06:00
deepend fe9a2ee202 Preparation for 2.18.3 2026-06-29 20:49:41 -06:00
deepend-tildeclub 5e27dc7e9f Merge pull request #315 from ZoiteChat/python-script-compatibility-fix
Snapshot Python hooks during plugin unload
2026-06-29 20:41:00 -06:00
deepend-tildeclub 69d083e3c0 Merge pull request #314 from ZoiteChat/openssl4-build-fixes
Support OpenSSL 4 APIs
2026-06-29 20:38:39 -06:00
deepend 2b95f9724e Snapshot Python hooks during plugin unload 2026-06-29 19:13:18 -06:00
deepend 4066a07892 Support OpenSSL 4 API 2026-06-29 18:47:46 -06:00
deepend-tildeclub 443ab58f65 Merge pull request #313 from ZoiteChat/fix-lag-ping-timeout-window
Fix lag ping timeout window.
2026-06-27 11:56:38 -06:00
deepend e0116acbe4 Fix lag ping timeout window. 2026-06-27 11:46:51 -06:00
deepend-tildeclub 91e9bf0207 Merge pull request #311 from ZoiteChat/fix-pm-echo-routing
Fix self-echoed PM routing to target tab
2026-06-27 01:59:20 -06:00
deepend aff0335f7e Fix self-echoed PM routing to target tab 2026-06-27 01:42:23 -06:00
deepend-tildeclub 4d8baaa19b Merge pull request #310 from ZoiteChat/fix-tray-right-click-menu
Fix Windows GtkStatusIcon tray menu popup
2026-06-26 14:47:23 -06:00
deepend 65452d80f1 Use native Win32 tray context menu 2026-06-26 14:32:12 -06:00
deepend-tildeclub df4d432151 Merge pull request #308 from ZoiteChat/replace-depreciated-ipv4-conversion-calls
Replace legacy IPv4 APIs
2026-06-25 21:34:39 -06:00
deepend-tildeclub 1390f2c783 Merge pull request #307 from ZoiteChat/ircv3-typing-reply-messagetags-echo-message
IRCv3 +message-tags, +echo-message, +typing +replies
2026-06-25 20:32:24 -06:00
deepend be99d0e900 Add reply cache, composer bar, and nick-menu replies 2026-06-25 14:51:04 -06:00
deepend 97fcf1d5fc Replace legacy IPv4 APIs 2026-06-24 14:24:43 -06:00
deepend c9b14d7c45 fix github bug template 2026-06-22 16:20:46 -06:00
deepend 3d0dda0347 Add github bug report template 2026-06-22 16:17:24 -06:00
deepend-tildeclub 8d69b35d67 Merge pull request #303 from ZoiteChat/fix-invalid-cert-setting
Restore accept-invalid-cert TLS bypass behavior
2026-06-22 16:00:55 -06:00
deepend 7ba142a9f2 Restore accept-invalid-cert TLS bypass behavior 2026-06-20 19:07:19 -06:00
deepend-tildeclub 56ac55806d Merge pull request #302 from ZoiteChat/theme-gtk3-tests-revert
Revert previous fixes merged to master without forcing.
2026-06-20 11:04:25 -06:00
deepend 47f53c14db Adjust Fedora package checks for GTK3 test stability 2026-06-20 10:59:10 -06:00
deepend a47d40b914 Guard GTK3 theme tests for headless Xwayland 2026-06-20 10:25:02 -06:00
deepend be33ae30bb Guard GTK theme ops when screen/seat missing 2026-06-20 09:51:16 -06:00
deepend 6d5f1306bf Guard legacy OpenSSL cleanup for 4.x builds 2026-06-20 09:24:05 -06:00
deepend ad3dc716e7 2.18.2 release preparation 2026-06-20 08:44:47 -06:00
deepend-tildeclub 089b125a89 Merge pull request #301 from ZoiteChat/mode-button-multi-line-settings
Add topic layout prefs for inline modes/multiline topics
2026-06-20 07:58:25 -06:00
deepend dfd4554a11 Move Topic Bar prefs under Appearance 2026-06-20 04:48:47 -06:00
deepend 1b98297881 Add topic layout prefs for inline modes/multiline topics 2026-06-20 04:09:45 -06:00
deepend-tildeclub cacc8fcafe Merge pull request #299 from ZoiteChat/fix-unload-crash
Fix DBus unload null filename path
2026-06-18 19:45:38 -06:00
deepend ac07d39db8 Fix DBus unload null filename path 2026-06-18 19:25:11 -06:00
deepend-tildeclub e27aa77cec Merge pull request #296 from ZoiteChat/fishlim-manager
Add FiSHLiM manager and context menu hooks
2026-06-18 19:11:11 -06:00
deepend cbc9f68dd6 Isolate GTK3 settings test data home + use xwayland-run 2026-06-18 18:57:49 -06:00
deepend 944d0f5a70 adding fedora packaging spec file. 2026-06-17 04:00:30 -06:00
deepend 9ded7ef830 Clarify FiSHLiM key manager invite wording 2026-06-16 15:47:56 -06:00
deepend adb18b7ce3 Improve FiSHLiM key delete flow + CBC warnings 2026-06-15 23:26:59 -06:00
deepend-tildeclub 8220e60257 Merge pull request #297 from ZoiteChat/fix-flatpak-tabs-tree-errors
Expose hidden channel tree for tab switcher compatibility
2026-06-15 23:01:03 -06:00
deepend b09af655e7 Add FiSHLiM GTK key manager + context menu access 2026-06-15 22:56:50 -06:00
deepend 5833f8a3d8 Expose hidden channel tree for tab switcher compatibility 2026-06-15 22:52:20 -06:00
deepend-tildeclub 72132d5e88 Merge pull request #283 from ZoiteChat/tray-iconified-restore
Fix tray restore for iconified windows
2026-06-15 14:22:47 -06:00
deepend-tildeclub 4167c4db78 Merge pull request #293 from ZoiteChat/feat-reset-keybinds
Add keybind reset while preserving custom rows
2026-06-12 10:37:36 -06:00
deepend 3e1f6b9137 Add keybind reset while preserving custom rows 2026-06-12 08:22:03 -06:00
deepend-tildeclub bb08b19c0d Merge pull request #291 from ZoiteChat/extend-keybind-change-page-targeting
Allow keybinds to switch by channel name
2026-06-10 16:28:18 -06:00
deepend-tildeclub 7497919e8f Merge pull request #290 from ZoiteChat/ssl-init--modern-apis
Harden TLS defaults, cert checks, and hostname failures
2026-06-10 16:24:19 -06:00
deepend-tildeclub 41cbe7b31c Merge pull request #287 from ZoiteChat/customize-all-keybinds
Customize all keybinds
2026-06-10 16:22:52 -06:00
deepend 425e951341 Allow keybinds to switch by channel name 2026-06-10 16:19:01 -06:00
deepend 2e4a0b92fc Harden TLS defaults, cert checks, and hostname failures 2026-06-10 16:13:59 -06:00
deepend 9f839579e2 Show live menu accelerators from keybinds 2026-06-10 15:57:23 -06:00
deepend 900066b9d4 Add option to hide join/part hostmasks 2026-06-10 15:09:47 -06:00
deepend 97534b0670 Add option to hide join/part hostmasks 2026-06-10 15:05:00 -06:00
deepend-tildeclub 62672ade04 Merge pull request #285 from ZoiteChat/no_proxy_option
Add -noproxy to /server
2026-06-10 09:05:39 -06:00
deepend b22e1c1ccc Add -noproxy to /server 2026-06-10 08:47:52 -06:00
deepend-tildeclub 1b8e60c26d Merge pull request #284 from ZoiteChat/windows-taskbar-improvement
Limit Win32 taskbar workaround to size state changes
2026-06-10 08:20:33 -06:00
deepend 035dfdd332 Limit Win32 taskbar workaround to size state changes 2026-06-09 14:02:41 -06:00
deepend d1707d3c72 Fix tray restore for iconified windows 2026-06-09 13:55:43 -06:00
deepend-tildeclub c9bed097ba Merge pull request #282 from ZoiteChat/improve-theme-switching-consistency
Fix GTK3 theme background refresh/saving
2026-06-09 11:37:44 -06:00
deepend 5acb90025f Fix GTK3 theme background refresh/saving 2026-06-09 11:23:38 -06:00
deepend-tildeclub 2d12da79b0 Merge pull request #279 from ZoiteChat/resolver-api-updates
Replace DCC IPv4 lookups with getaddrinfo
2026-06-06 20:21:42 -06:00
deepend-tildeclub bede379021 Merge pull request #267 from ZoiteChat/keyring-support
Keyring support
2026-06-06 15:54:47 -06:00
deepend 38acde1b20 Replace DCC IPv4 lookups with getaddrinfo 2026-06-06 15:50:52 -06:00
deepend d0c4a5addd Fix keyring mode switching and password reveal state 2026-06-05 10:38:16 -06:00
deepend-tildeclub b8a7ccf005 Merge pull request #277 from ZoiteChat/fix-window-restore
Defer topic relayout after restore-down
2026-06-05 09:15:10 -06:00
deepend 68fc53585e Defer topic relayout after restore-down 2026-06-04 15:24:37 -06:00
deepend 26afc125c7 Add optional keyring + encrypted password fallback 2026-06-02 15:57:28 -06:00
deepend-tildeclub 090b39f78b Merge pull request #266 from ZoiteChat/network-meter-alignment
Align text network meter columns
2026-06-02 15:41:57 -06:00
deepend 3722de6b13 Align text network meter columns 2026-06-02 14:49:29 -06:00
deepend-tildeclub d93c9aa780 Merge pull request #255 from ZoiteChat/kde-dialog-native-controls
Use native file chooser dialogs everywhere
2026-05-26 13:14:24 -06:00
deepend ea56504aee Use native file chooser dialogs everywhere 2026-05-26 02:24:06 -06:00
deepend 46b91edfdf Bundle Perl paths into AppImage runtime 2026-05-25 23:08:41 -06:00
deepend-tildeclub d2dfde519d Merge pull request #253 from ZoiteChat/timedate-tooltop
Add timestamp/date hover tooltips
2026-05-25 15:58:40 -06:00
deepend 1eac56f22c Add timestamp/date hover tooltips 2026-05-25 14:49:30 -06:00
deepend-tildeclub fb491a6bb2 Merge pull request #249 from ZoiteChat/lost-connection-fixes
Add configurable stale-link ping checks
2026-05-25 12:21:45 -06:00
deepend 3da7c89b66 Add configurable stale-link ping checks 2026-05-25 11:43:10 -06:00
deepend-tildeclub e842cf3a57 Merge pull request #248 from ZoiteChat/scroll-speed-fix
Fix xtext wheel scroll speed handling + prefs slider
2026-05-25 10:29:40 -06:00
deepend 85b0e8f1a6 Fix xtext wheel scroll speed handling + prefs slider 2026-05-25 09:45:22 -06:00
deepend-tildeclub b8e03ff6c1 Merge pull request #247 from ZoiteChat/fishlim-fixes
Fix FiSHLiM OpenSSL provider/context init issues
2026-05-25 02:24:32 -06:00
deepend 54b1703d67 Make FiSHLiM OpenSSL provider loading non-fatal 2026-05-25 02:13:24 -06:00
deepend 15d647a0ec Fix detached tab reattach UAF crash path 2026-05-22 14:50:44 -06:00
deepend-tildeclub 353558ddb3 Merge pull request #243 from sney/project-readme
Project readme updates
2026-05-22 09:24:37 -06:00
Jesse Rhodes 9f58d30050 Add more short description. 2026-05-22 09:47:13 -04:00
Jesse Rhodes 4f0632cdf1 Update existing links. 2026-05-22 09:44:29 -04:00
Jesse Rhodes ff8ba71948 Remove link to incomplete/outdated (?) troubleshooting guide. 2026-05-22 09:41:23 -04:00
Jesse Rhodes a367591327 Fix download link 404. 2026-05-22 09:38:07 -04:00
Jesse Rhodes c91925fbc2 Match intro to distro package short descriptions. 2026-05-22 09:36:44 -04:00
deepend 479f1649ef Bump release metadata to 2.18.1 2026-05-21 18:11:33 -06:00
deepend-tildeclub 06f69184b6 Merge pull request #239 from ZoiteChat/size_t_int_fixes
Size t int fixes
2026-05-21 17:10:07 -06:00
deepend-tildeclub 3471d9a57c Merge pull request #241 from ZoiteChat/win32-relax-arch-check
Use x64compatible for Windows installer arch checks
2026-05-21 17:09:51 -06:00
deepend 28d4035477 Use x64compatible for Windows installer arch checks 2026-05-21 02:50:49 -06:00
deepend-tildeclub 3e1d151efd Merge pull request #240 from ZoiteChat/win32-dependency-update
Use new GTK3 bundle zip in Windows CI
2026-05-21 02:21:51 -06:00
deepend 556cfc3036 Use new GTK3 bundle zip in Windows CI 2026-05-20 21:25:37 -06:00
deepend c49b757be6 Clamp Win32 sysinfo UTF length casts 2026-05-20 15:51:07 -06:00
deepend cec7e2caf3 Use size_t for strlen length temporaries 2026-05-20 15:34:19 -06:00
deepend 9a0c07a461 Clean up Win32 size/length casts 2026-05-20 15:22:06 -06:00
deepend c7064c18b9 Clamp spell-provider length casts on Win32 2026-05-20 14:51:06 -06:00
deepend-tildeclub fb897310c8 Merge pull request #238 from ZoiteChat/win32-taskbar-icon-identity
Set Windows AppUserModelID before GTK startup
2026-05-20 11:56:38 -06:00
deepend 5944849326 Guard fullscreen menu sync against null session 2026-05-20 10:34:35 -06:00
deepend 1255f1e6c7 Fix Win taskbar toggle restore check 2026-05-20 10:19:51 -06:00
deepend d7bc09d859 Fix Win minimize-to-tray taskbar bounce 2026-05-20 09:46:33 -06:00
deepend f84a448351 Fix Win tray restore timer loop/reentry 2026-05-19 21:39:02 -06:00
deepend 7e34690e0c Set Windows AppUserModelID before GTK startup 2026-05-19 17:20:08 -06:00
deepend-tildeclub 6f6d378600 Merge pull request #237 from ZoiteChat/ddh-null-gdkwindow
Guard GTK drag/drop handlers against null GdkWindow
2026-05-19 15:55:21 -06:00
deepend-tildeclub 216b463b8f Merge pull request #236 from ZoiteChat/native-dialog-box
Make native file chooser modal on Windows
2026-05-19 15:55:01 -06:00
deepend 4ad84cb5e5 Guard GTK drag/drop handlers against null GdkWindow 2026-05-19 15:46:06 -06:00
deepend 19e0946717 Make native file chooser modal on Windows 2026-05-19 15:11:55 -06:00
deepend 0de1ad06cd Null-safe /set string preference rendering 2026-05-19 15:07:21 -06:00
deepend-tildeclub 72427006dd Merge pull request #231 from mlt/py-eol
fix(python): concatenate bytes in compile_line of py console
2026-05-12 15:34:31 -06:00
deepend-tildeclub 0e5f702651 Merge pull request #233 from ZoiteChat/userlist-button-meter-layout-fix
Tighten userlist button/meter layout
2026-05-11 08:17:02 -06:00
deepend 4f1b0fc838 Tighten userlist button/meter layout 2026-05-11 01:35:57 -06:00
Mikhail Titov 23d0963c2d fix(python): decode the console input to str in _on_say_command 2026-05-10 23:57:43 -05:00
deepend-tildeclub dcb35fb80f Merge pull request #230 from ZoiteChat/fix-scrollbar-consistency
UI Consistency: Use native GTK scrollbars across main panes
2026-05-10 08:31:15 -06:00
deepend b1768854c3 Tighten userlist count/pane width behavior 2026-05-09 12:53:35 -06:00
deepend 4ed4eaf8e8 fix FSF address. 2026-05-08 20:50:51 -06:00
deepend-tildeclub d167b53b17 Merge pull request #227 from ZoiteChat/fix-tab-scroll-skip
Consume handled tab wheel events
2026-05-08 07:52:37 -06:00
deepend 18eff80a30 UI Consistency: Use native GTK scrollbars across main panes 2026-05-07 13:29:22 -06:00
deepend a44ec5f624 Consume handled tab wheel events 2026-05-07 12:03:41 -06:00
deepend-tildeclub 5da518f50e Merge pull request #226 from mlt/close-file-stream
fix(checksum): Make sure file stream is closed
2026-05-07 09:21:28 -06:00
Mikhail Titov ebb11a8ac5 fix(checksum): Make sure file stream is closed 2026-05-07 09:11:10 -05:00
deepend-tildeclub dd9c5db1aa Merge pull request #224 from ZoiteChat/channel_switcher_mouse_wheel_fix
Fix and Enable mouse wheel channel switching by default
2026-05-06 13:35:13 -06:00
deepend 984ac9763e Fix and Enable mouse wheel channel switching by default 2026-05-06 13:10:30 -06:00
deepend-tildeclub ad67af2f8f Merge pull request #222 from ZoiteChat/fix-autocorrect-replace
Fix auto-replace to respect whole-word matches
2026-05-05 17:32:29 -06:00
deepend cc8460d366 Fix auto-replace to respect whole-word matches 2026-05-05 17:22:56 -06:00
deepend 1dc9b9c956 Use Lua 5.4 deps in CI 2026-04-29 16:21:01 -06:00
deepend-tildeclub 20b552ad9d Merge pull request #214 from ZoiteChat/bump_perl_version_5_42_2
flatpak: bump perl to 5.42.2
2026-04-29 16:15:42 -06:00
deepend-tildeclub 12f4885ad7 Merge pull request #217 from ZoiteChat/remove-winamp-plugin
Remove Winamp Plugin.
2026-04-29 16:14:59 -06:00
deepend 9c1fdef286 Default Lua pkg-config target to lua-5.4 2026-04-29 16:10:06 -06:00
deepend c125a154da More missed Winamp references. 2026-04-29 16:08:42 -06:00
deepend 028d768b23 Remove another Winamp reference. 2026-04-29 15:57:19 -06:00
deepend 42972e01b0 Remove Winamp Plugin. 2026-04-29 15:42:11 -06:00
deepend 1bd37af7d0 Refresh menu sizing after theme switch 2026-04-27 15:36:22 -06:00
deepend 5fad2d0c02 flatpak: bump perl to 5.42.2 2026-04-27 13:43:13 -06:00
deepend-tildeclub bd9adeb795 Merge pull request #211 from ZoiteChat/gtk-state-mismatch
Fix palette color reads to preserve base GTK state
2026-04-27 13:40:28 -06:00
deepend-tildeclub 5fd3c7c9d9 Merge pull request #210 from ZoiteChat/dbus_gdbus_migration
Migrate to gdbus.
2026-04-27 13:38:52 -06:00
deepend 937184429f Drop leftover dbus-glib refs/deps 2026-04-27 13:30:24 -06:00
deepend e31ccff3f8 Fix palette color reads to preserve base GTK state 2026-04-27 13:26:53 -06:00
deepend edaafba7eb Drop dbus-glib deps, port example to GDBus 2026-04-27 13:13:16 -06:00
deepend 87018fa7fe Migrate to gdbus. 2026-04-27 11:04:04 -06:00
deepend 8fcec519d0 Fix tab section scrolling on overflow 2026-04-27 10:44:12 -06:00
deepend aa52236c01 New Release: 2.18.0 2026-04-20 11:56:03 -06:00
deepend cd5b61d9d0 Use KiB/MiB units for DCC speed limits 2026-04-20 10:38:22 -06:00
deepend 4eefdd6011 Stop palette CSS from styling radio rings 2026-04-20 10:32:54 -06:00
deepend-tildeclub 21748b3133 Merge pull request #204 from ZoiteChat/flatpak-improvements
Tighten Flatpak perms, pin lgi source
2026-04-16 10:53:49 -06:00
deepend-tildeclub e7db202f88 Merge pull request #207 from ZoiteChat/domain-url-parsing
Validate host links with PSL rules
2026-04-15 02:36:09 -06:00
deepend a93e76eaf4 Add PSL fetch step to Flatpak CI 2026-04-14 15:56:43 -06:00
deepend 3c5bdb2ab2 Revert Meson PSL input wiring, keep generator fallback logic 2026-04-14 09:53:16 -06:00
deepend 8f7c40caf1 Validate host links with PSL rules 2026-04-13 20:10:28 -06:00
deepend 150ad62771 Fix GTK populate test stub linkage 2026-04-13 14:54:59 -06:00
deepend-tildeclub 5e3715dc31 Merge pull request #206 from ZoiteChat/close-undo-close-tabs
Add Ctrl+W close + Ctrl+Shift+T tab reopen
2026-04-13 11:44:44 -06:00
deepend 05648ba8e3 Add Ctrl+W close + Ctrl+Shift+T tab reopen 2026-04-13 10:55:59 -06:00
deepend-tildeclub 0956471a10 Merge pull request #205 from ZoiteChat/fix-nickname-in-topic-linking
Tighten topic link detect/open rules
2026-04-13 07:57:26 -06:00
deepend 1f79e62f53 Tighten topic link detect/open rules 2026-04-13 00:08:00 -06:00
deepend-tildeclub c123e93704 Merge pull request #203 from ZoiteChat/fix-network-tree-collapse-channel-focus
Ignore collapsed parent selects in chanview tree
2026-04-12 23:50:59 -06:00
deepend e49333340f Tighten Flatpak perms, pin lgi source 2026-04-12 23:43:12 -06:00
deepend 7210f16dfc Ignore collapsed parent selects in chanview tree 2026-04-12 23:09:52 -06:00
deepend-tildeclub 2825e992df Merge pull request #201 from ZoiteChat/fix-notification-icon
fix notification icon + fix flatpak screenshot
2026-04-12 21:00:37 -06:00
deepend 8e8f534c21 fix notification icon + fix flatpak screenshot 2026-04-12 20:46:47 -06:00
deepend d3d5ac905c Enabled colored nicknames in the userlist by default 2026-04-12 15:41:20 -06:00
deepend-tildeclub 4cca23a283 Merge pull request #198 from ZoiteChat/Expand_palette_and_selection_CSS
Expand palette and selection css
2026-04-11 09:07:21 -06:00
deepend 0f3fffc728 Broaden GTK3 palette/state selector coveragez even more. 2026-04-10 15:40:58 -06:00
deepend 7261d8637d Expand palette CSS + selection state coverage 2026-04-10 14:12:14 -06:00
deepend-tildeclub 8a1e82986a Merge pull request #197 from ZoiteChat/pevents_import_from_hct
Import pevents from .hct too, show clearer result dialog
2026-04-09 13:23:17 -06:00
deepend-tildeclub e8d421b3df Merge pull request #196 from ZoiteChat/topic_bar_height
Auto-size topic bar to wrapped text
2026-04-09 13:23:03 -06:00
deepend cb2c09b2ba Import pevents from .hct too, show clearer result dialog 2026-04-09 12:30:23 -06:00
deepend a14026f0d1 Add vertical padding to topic text view 2026-04-09 10:56:39 -06:00
deepend e237df31f4 Auto-size topic bar to wrapped text 2026-04-09 10:03:59 -06:00
deepend-tildeclub a9421c63a9 Merge pull request #195 from ZoiteChat/hct_import_support
Add .hct support to colors.conf import
2026-04-09 08:55:48 -06:00
deepend 4f2938d957 Add .hct support to colors.conf import 2026-04-08 17:47:51 -06:00
deepend-tildeclub 8fa9d3678b Merge pull request #194 from ZoiteChat/add-python-perl-lua-versions-to-installer
Label installer runtimes with exact versions
2026-04-07 09:16:41 -06:00
deepend 4bbc23c185 Label installer runtimes with exact versions 2026-04-07 08:42:23 -06:00
deepend-tildeclub 73733e4176 Merge pull request #193 from ZoiteChat/pasted_path_fix
Refine cmd-char parsing for pasted path text
2026-04-06 15:17:37 -06:00
deepend 9f8faf0a6e Refine cmd-char parsing for pasted path text mentioned at https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=897426 2026-04-06 14:26:47 -06:00
deepend-tildeclub 5ff704459c Merge pull request #192 from sney/use-appstream
Improve appstream metainfo validation
2026-04-06 14:00:52 -06:00
Jesse Rhodes 0ccc730d85 test: Validate plugin metainfo. 2026-04-05 10:35:54 -04:00
Jesse Rhodes 093ffe791e test: Switch to appstreamcli for metainfo validation. 2026-04-05 10:31:19 -04:00
deepend b9ff051043 Drop forced bold from userlist prefixes 2026-04-03 08:15:38 -06:00
deepend 10b6d5927b Tighten tab layout, right-align close button 2026-04-03 07:54:01 -06:00
deepend 0052b6589e updated svg. removed unused svg files 2026-04-01 22:52:27 -06:00
deepend 51e9ac15df fix zoitechat.svg 2026-04-01 00:21:44 -06:00
deepend-tildeclub 4df579e0d3 Merge pull request #185 from ZoiteChat/improve-appimage-URI-folder-opening
Sanitize Linux open env for AppImage, add safer fallbacks
2026-04-01 00:15:53 -06:00
deepend 2cf69d2e67 Sanitize Linux open env for AppImage, add safer fallbacks 2026-03-31 23:26:40 -06:00
deepend-tildeclub 64bffd0dc8 Merge pull request #184 from ZoiteChat/add-gtk3-themes-windows-installer
Theme: Adding themes users can select at install time.
2026-03-31 19:38:40 -06:00
deepend-tildeclub b451240dfd Merge pull request #181 from ZoiteChat/prefs-option-disable-tab-close-button
Add pref for tab close buttons
2026-03-31 19:30:26 -06:00
deepend 41cc1f617f Theme: Adding themes users can select at install time. 2026-03-31 19:28:45 -06:00
deepend ed671eac25 Add pref for tab close buttons 2026-03-31 18:10:28 -06:00
deepend 9f2529adb6 Update all links to new domain. 2026-03-30 18:25:26 -06:00
deepend-tildeclub 3903d7b9e5 Change official documentation link in README
Updated the link for the official documentation from zoitechat.zoite.net to zoitechat.org.
2026-03-30 17:16:01 -06:00
261 changed files with 70885 additions and 4567 deletions

No files matched your search

+132
View File
@@ -0,0 +1,132 @@
---
name: Bug report
about: Report a problem with ZoiteChat
title: "[Bug]: "
labels: bug
assignees: ""
---
```
+------------------------------------------------------------+
| ZoiteChat Bug Report |
| Please fill this out so the bug goblin has enough crumbs. |
+------------------------------------------------------------+
```
## Summary
What went wrong?
```text
Describe the bug clearly and briefly.
```
## Steps to Reproduce
```text
1.
2.
3.
4.
```
## Expected Result
```text
What should have happened?
```
## Actual Result
```text
What happened instead?
```
## ZoiteChat Version
```text
ZoiteChat version:
Build/source: GitHub release / Fedora package / source build / Windows installer / other
Commit hash, if built from source:
```
## System Information
```text
OS:
OS version:
Desktop environment:
Display server: Wayland / X11 / Windows / other
CPU architecture: x86_64 / aarch64 / other
```
## IRC / Network Details
```text
IRC network/server:
Port:
TLS/SSL enabled: yes / no
SASL enabled: yes / no
Using bouncer/ZNC: yes / no
Proxy/VPN involved: yes / no
IRCv3 capabilities involved, if known:
```
## Plugins, Scripts, and Config
```text
Plugins enabled:
Scripts loaded:
Theme/custom CSS:
Custom config changes:
Does the bug still happen with a clean config? yes / no / untested
```
## Logs / Terminal Output
Paste relevant output below.
```text
```
## Raw IRC Lines, If Relevant
Remove passwords, tokens, IPs, private channels, private messages, certs, and account details before posting.
```text
```
## Crash Details, If Relevant
```text
Did ZoiteChat crash? yes / no
Crash reporter output:
Backtrace:
Core dump available: yes / no
```
## Screenshots
Attach screenshots or screen recordings if the issue is visual.
## Regression Check
```text
Did this work in an older version? yes / no / unknown
Last known working version:
First broken version:
```
## Checklist
* [ ] I searched existing issues first.
* [ ] I tested with the latest ZoiteChat version available to me.
* [ ] I included steps to reproduce the issue.
* [ ] I removed private information from logs and raw IRC lines.
* [ ] I included system and network details where relevant.
## Extra Notes
```text
Anything else that might help.
```
+1
View File
@@ -0,0 +1 @@
blank_issues_enabled: false
+57
View File
@@ -0,0 +1,57 @@
---
name: Feature request
about: Suggest an improvement for ZoiteChat
title: "[Feature]: "
labels: enhancement
assignees: ""
---
## Summary
```text
Describe the feature or enhancement clearly and briefly.
```
## Problem
```text
What problem would this solve?
```
## Proposed Solution
```text
How should ZoiteChat behave?
```
## Alternatives Considered
```text
Other approaches or workarounds you considered.
```
## Use Case
```text
Who benefits from this, and when would they use it?
```
## Platform Details
```text
OS:
ZoiteChat version:
Build/source: GitHub release / Fedora package / source build / Windows installer / other
```
## Checklist
* [ ] I searched existing issues first.
* [ ] I checked whether this already exists in the latest ZoiteChat version available to me.
* [ ] I explained the problem this would solve.
## Extra Notes
```text
Anything else that might help.
```
+37
View File
@@ -0,0 +1,37 @@
version: 2
updates:
# Flatpak shared modules
- package-ecosystem: "gitsubmodule"
directory: "/"
schedule:
interval: "weekly"
day: "monday"
open-pull-requests-limit: 2
labels:
- "dependencies"
# GitHub Actions dependencies
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
day: "monday"
open-pull-requests-limit: 3
labels:
- "dependencies"
commit-message:
prefix: "ci"
cooldown:
default-days: 7
groups:
actions-minor-patch:
patterns:
- "*"
update-types:
- "minor"
- "patch"
+64 -28
View File
@@ -2,7 +2,7 @@ name: AppImage Build
on: on:
push: push:
branches: [master] branches: [master, 'release/**']
pull_request: pull_request:
branches: [master] branches: [master]
@@ -17,7 +17,7 @@ jobs:
artifact-metadata: write artifact-metadata: write
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v7
with: with:
submodules: true submodules: true
@@ -28,15 +28,15 @@ jobs:
sudo apt-get install -y --no-install-recommends \ sudo apt-get install -y --no-install-recommends \
build-essential pkg-config meson ninja-build cmake \ build-essential pkg-config meson ninja-build cmake \
gettext \ gettext \
libcanberra-dev libdbus-glib-1-dev libglib2.0-dev \ libcanberra-dev libglib2.0-dev \
libarchive-dev \ libarchive-dev \
libgtk-3-dev \ libgtk-3-dev libfontconfig1-dev libpango1.0-dev xvfb xauth dbus-daemon at-spi2-core \
libwayland-client0 libwayland-cursor0 libwayland-egl1 \ libwayland-client0 libwayland-cursor0 libwayland-egl1 \
libxkbcommon0 \ libxkbcommon0 \
libgtk-3-bin libglib2.0-bin shared-mime-info gsettings-desktop-schemas \ libgtk-3-bin libglib2.0-bin glib-networking shared-mime-info gsettings-desktop-schemas \
libluajit-5.1-dev libpci-dev libperl-dev libssl-dev libayatana-appindicator3-dev \ liblua5.4-dev libpci-dev libperl-dev libssl-dev libayatana-appindicator3-dev \
perl python3 python3-minimal python3-dev python3-cffi mono-devel desktop-file-utils \ perl python3 python3-minimal python3-dev python3-cffi python3-sphinx mono-devel desktop-file-utils \
fonts-noto-color-emoji breeze-gtk-theme \ breeze-gtk-theme \
patchelf file curl patchelf file curl
- name: Configure - name: Configure
@@ -45,6 +45,7 @@ jobs:
rm -rf build rm -rf build
meson setup build \ meson setup build \
--prefix=/usr \ --prefix=/usr \
-Dinstall-docs=true \
-Dtext-frontend=true \ -Dtext-frontend=true \
-Dwith-perl=perl \ -Dwith-perl=perl \
-Dwith-python=python3 \ -Dwith-python=python3 \
@@ -56,6 +57,9 @@ jobs:
set -eux set -eux
ninja -C build ninja -C build
- name: Test emoji catalog, font coverage and picker
run: xvfb-run -a dbus-run-session -- meson test -C build --print-errorlogs "Emoji Data Tests" "Emoji Font Tests" "Emoji UI Tests"
- name: Install to AppDir - name: Install to AppDir
run: | run: |
set -eux set -eux
@@ -86,11 +90,21 @@ jobs:
cp -a /usr/lib/x86_64-linux-gnu/python3/dist-packages AppDir/usr/lib/x86_64-linux-gnu/python3/ cp -a /usr/lib/x86_64-linux-gnu/python3/dist-packages AppDir/usr/lib/x86_64-linux-gnu/python3/
fi fi
if [ -d "/usr/lib/x86_64-linux-gnu/perl-base" ]; then
install -d AppDir/usr/lib/x86_64-linux-gnu
cp -a /usr/lib/x86_64-linux-gnu/perl-base AppDir/usr/lib/x86_64-linux-gnu/
fi
if [ -d "/usr/lib/x86_64-linux-gnu/perl" ]; then if [ -d "/usr/lib/x86_64-linux-gnu/perl" ]; then
install -d AppDir/usr/lib/x86_64-linux-gnu install -d AppDir/usr/lib/x86_64-linux-gnu
cp -a /usr/lib/x86_64-linux-gnu/perl AppDir/usr/lib/x86_64-linux-gnu/ cp -a /usr/lib/x86_64-linux-gnu/perl AppDir/usr/lib/x86_64-linux-gnu/
fi fi
if [ -d "/usr/lib/x86_64-linux-gnu/perl5" ]; then
install -d AppDir/usr/lib/x86_64-linux-gnu
cp -a /usr/lib/x86_64-linux-gnu/perl5 AppDir/usr/lib/x86_64-linux-gnu/
fi
if [ -d "/usr/share/perl" ]; then if [ -d "/usr/share/perl" ]; then
install -d AppDir/usr/share install -d AppDir/usr/share
cp -a /usr/share/perl AppDir/usr/share/ cp -a /usr/share/perl AppDir/usr/share/
@@ -100,20 +114,17 @@ jobs:
install -d AppDir/usr/share install -d AppDir/usr/share
cp -a /usr/share/perl5 AppDir/usr/share/ cp -a /usr/share/perl5 AppDir/usr/share/
fi fi
perl -MFile::Spec -e 'print "Build host File::Spec: $INC{\"File/Spec.pm\"}\n"'
find AppDir/usr -path '*/File/Spec.pm' -print -quit | grep -q .
if compgen -G '/usr/lib/x86_64-linux-gnu/libpython3*.so*' > /dev/null; then if compgen -G '/usr/lib/x86_64-linux-gnu/libpython3*.so*' > /dev/null; then
install -d AppDir/usr/lib/x86_64-linux-gnu install -d AppDir/usr/lib/x86_64-linux-gnu
cp -a /usr/lib/x86_64-linux-gnu/libpython3*.so* AppDir/usr/lib/x86_64-linux-gnu/ cp -a /usr/lib/x86_64-linux-gnu/libpython3*.so* AppDir/usr/lib/x86_64-linux-gnu/
fi fi
if [ -d "/usr/share/gtk-3.0/emoji" ]; then # Every package ships the pinned font, including portable builds.
install -d AppDir/usr/share/gtk-3.0 cmp data/fonts/NotoColorEmoji.ttf AppDir/usr/share/fonts/zoitechat/NotoColorEmoji.ttf
cp -a /usr/share/gtk-3.0/emoji AppDir/usr/share/gtk-3.0/
fi
if [ -f "/usr/share/fonts/truetype/noto/NotoColorEmoji.ttf" ]; then
install -d AppDir/usr/share/fonts/truetype/noto
cp -a /usr/share/fonts/truetype/noto/NotoColorEmoji.ttf AppDir/usr/share/fonts/truetype/noto/
fi
if [ -d "/usr/lib/x86_64-linux-gnu/gtk-3.0/modules" ]; then if [ -d "/usr/lib/x86_64-linux-gnu/gtk-3.0/modules" ]; then
install -d AppDir/usr/lib/x86_64-linux-gnu/gtk-3.0 install -d AppDir/usr/lib/x86_64-linux-gnu/gtk-3.0
@@ -125,6 +136,20 @@ jobs:
cp -a /usr/lib/gtk-3.0/modules AppDir/usr/lib/gtk-3.0/ cp -a /usr/lib/gtk-3.0/modules AppDir/usr/lib/gtk-3.0/
fi fi
# HTTPS inline-image downloads use GSocketClient TLS on Linux.
# GLib provides the API, but TLS and system-proxy support live in
# loadable GIO modules shipped by glib-networking. Bundle those
# modules so the AppImage does not depend on the host copy.
gio_module_dir="$(pkg-config --variable=giomoduledir gio-2.0)"
test -n "$gio_module_dir"
test -f "${gio_module_dir}/libgiognutls.so"
install -d "AppDir${gio_module_dir}"
cp -a "${gio_module_dir}"/libgio*.so "AppDir${gio_module_dir}/"
gio-querymodules "AppDir${gio_module_dir}"
test -f "AppDir${gio_module_dir}/libgiognutls.so"
test -s "AppDir${gio_module_dir}/giomodule.cache"
- name: Verify bundled plugins - name: Verify bundled plugins
run: | run: |
set -eux set -eux
@@ -162,7 +187,7 @@ jobs:
APPDIR="${APPDIR:-$(dirname "$(readlink -f "$0")")}" APPDIR="${APPDIR:-$(dirname "$(readlink -f "$0")")}"
export PATH="${PATH:-/usr/bin:/bin}:$APPDIR/usr/bin" export PATH="$APPDIR/usr/bin:${PATH:-/usr/bin:/bin}"
export LD_LIBRARY_PATH="$APPDIR/usr/lib:$APPDIR/usr/lib/x86_64-linux-gnu:${LD_LIBRARY_PATH:-}" export LD_LIBRARY_PATH="$APPDIR/usr/lib:$APPDIR/usr/lib/x86_64-linux-gnu:${LD_LIBRARY_PATH:-}"
export XDG_DATA_DIRS="$APPDIR/usr/share:${XDG_DATA_DIRS:-/usr/local/share:/usr/share}" export XDG_DATA_DIRS="$APPDIR/usr/share:${XDG_DATA_DIRS:-/usr/local/share:/usr/share}"
export GTK_EXE_PREFIX="$APPDIR/usr" export GTK_EXE_PREFIX="$APPDIR/usr"
@@ -178,14 +203,6 @@ jobs:
export GTK_PATH="$gtk_path_entries${GTK_PATH:+:$GTK_PATH}" export GTK_PATH="$gtk_path_entries${GTK_PATH:+:$GTK_PATH}"
fi fi
if [ -d "$APPDIR/etc/fonts" ]; then
export FONTCONFIG_SYSROOT="$APPDIR"
export FONTCONFIG_PATH="$APPDIR/etc/fonts${FONTCONFIG_PATH:+:$FONTCONFIG_PATH}"
if [ -f "$APPDIR/etc/fonts/fonts.conf" ]; then
export FONTCONFIG_FILE="$APPDIR/etc/fonts/fonts.conf"
fi
fi
if [ -d "$APPDIR/usr/lib/x86_64-linux-gnu/zoitechat/plugins" ]; then if [ -d "$APPDIR/usr/lib/x86_64-linux-gnu/zoitechat/plugins" ]; then
export ZOITECHAT_LIBDIR="$APPDIR/usr/lib/x86_64-linux-gnu/zoitechat/plugins" export ZOITECHAT_LIBDIR="$APPDIR/usr/lib/x86_64-linux-gnu/zoitechat/plugins"
elif [ -d "$APPDIR/usr/lib/zoitechat/plugins" ]; then elif [ -d "$APPDIR/usr/lib/zoitechat/plugins" ]; then
@@ -211,6 +228,23 @@ jobs:
unset GTK_MODULES unset GTK_MODULES
perl5lib_entries=""
for dir in \
"$APPDIR/usr/lib/x86_64-linux-gnu/perl-base" \
"$APPDIR/usr/lib/x86_64-linux-gnu/perl"/* \
"$APPDIR/usr/lib/x86_64-linux-gnu/perl5"/* \
"$APPDIR/usr/share/perl"/* \
"$APPDIR/usr/share/perl5"
do
if [ -d "$dir" ]; then
perl5lib_entries="${perl5lib_entries:+$perl5lib_entries:}$dir"
fi
done
if [ -n "$perl5lib_entries" ]; then
export PERL5LIB="$perl5lib_entries${PERL5LIB:+:$PERL5LIB}"
fi
export PYTHONHOME="$APPDIR/usr" export PYTHONHOME="$APPDIR/usr"
python_stdlib_dir="$(find "$APPDIR/usr/lib" -maxdepth 1 -type d -name 'python3.*' | head -n 1 || true)" python_stdlib_dir="$(find "$APPDIR/usr/lib" -maxdepth 1 -type d -name 'python3.*' | head -n 1 || true)"
pythonpath_entries="" pythonpath_entries=""
@@ -227,6 +261,9 @@ jobs:
export PYTHONPATH="$pythonpath_entries${PYTHONPATH:+:$PYTHONPATH}" export PYTHONPATH="$pythonpath_entries${PYTHONPATH:+:$PYTHONPATH}"
fi fi
# Register the private font without hiding the host's normal fonts.
export ZOITECHAT_EMOJI_FONT="${ZOITECHAT_EMOJI_FONT:-$APPDIR/usr/share/fonts/zoitechat/NotoColorEmoji.ttf}"
# OpenSSL trust store override # OpenSSL trust store override
export SSL_CERT_FILE="${SSL_CERT_FILE:-$APPDIR/etc/ssl/certs/ca-certificates.crt}" export SSL_CERT_FILE="${SSL_CERT_FILE:-$APPDIR/etc/ssl/certs/ca-certificates.crt}"
export SSL_CERT_DIR="${SSL_CERT_DIR:-$APPDIR/etc/ssl/certs}" export SSL_CERT_DIR="${SSL_CERT_DIR:-$APPDIR/etc/ssl/certs}"
@@ -256,7 +293,6 @@ jobs:
EOF EOF
chmod +x AppRun chmod +x AppRun
VERSION="$(git describe --tags --always)" VERSION="$(git describe --tags --always)"
./linuxdeploy-x86_64.AppImage \ ./linuxdeploy-x86_64.AppImage \
@@ -277,7 +313,7 @@ jobs:
subject-path: Zoitechat-*-x86_64.AppImage subject-path: Zoitechat-*-x86_64.AppImage
- name: Upload AppImage artifact - name: Upload AppImage artifact
uses: actions/upload-artifact@v6 uses: actions/upload-artifact@v7
with: with:
name: zoitechat-appimage name: zoitechat-appimage
path: Zoitechat-*-x86_64.AppImage path: Zoitechat-*-x86_64.AppImage
+6 -3
View File
@@ -2,7 +2,7 @@ name: Flatpak Build
on: on:
push: push:
branches: [master] branches: [master, 'release/**']
pull_request: pull_request:
branches: [master] branches: [master]
@@ -21,10 +21,13 @@ jobs:
options: --privileged options: --privileged
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v7
with: with:
submodules: true submodules: true
- name: Generate PSL list
run: curl -fsSL https://publicsuffix.org/list/public_suffix_list.dat -o src/common/public_suffix_list.dat
- name: Build Flatpak - name: Build Flatpak
id: flatpak_builder id: flatpak_builder
uses: flatpak/flatpak-github-actions/flatpak-builder@v6 uses: flatpak/flatpak-github-actions/flatpak-builder@v6
@@ -36,7 +39,7 @@ jobs:
- name: Upload Flatpak Bundle - name: Upload Flatpak Bundle
id: upload_flatpak id: upload_flatpak
uses: actions/upload-artifact@v6 uses: actions/upload-artifact@v7
with: with:
name: zoitechat.flatpak name: zoitechat.flatpak
path: zoitechat.flatpak path: zoitechat.flatpak
+168
View File
@@ -0,0 +1,168 @@
name: macOS build
on:
workflow_dispatch:
inputs:
sign_and_notarize:
description: 'Sign with Developer ID and notarize using release credentials'
type: boolean
default: false
push:
branches: [master]
tags: ['v*']
paths:
- 'src/**'
- 'plugins/**'
- 'data/**'
- 'tools/**'
- 'po/**'
- 'meson*'
- 'VERSION'
- 'packaging/macos/**'
- '.github/workflows/macos-build.yml'
pull_request:
paths:
- 'src/**'
- 'plugins/**'
- 'data/**'
- 'tools/**'
- 'po/**'
- 'meson*'
- 'VERSION'
- 'packaging/macos/**'
- '.github/workflows/macos-build.yml'
permissions:
contents: read
jobs:
build:
if: >-
github.event_name == 'pull_request' ||
(github.event_name == 'push' && !startsWith(github.ref, 'refs/tags/')) ||
(github.event_name == 'workflow_dispatch' && !inputs.sign_and_notarize)
name: Native ${{ matrix.arch }} bundle
strategy:
fail-fast: false
matrix:
include:
- runner: macos-15
arch: arm64
- runner: macos-15-intel
arch: x86_64
runs-on: ${{ matrix.runner }}
timeout-minutes: 90
env:
HOMEBREW_NO_AUTO_UPDATE: '1'
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install native dependencies
run: brew bundle --file packaging/macos/Brewfile
- name: Test packaging logic
run: python3 -m unittest discover -s packaging/macos/tests -v
- name: Build and test application
run: python3 packaging/macos/build.py --build-number '${{ github.run_number }}'
- name: Verify relocated startup
run: python3 packaging/macos/smoke.py dist/ZoiteChat.app
# Desktop capture can be unavailable on a runner image. Preserve the
# existing build gate and publish the separate visual-check outcome.
- name: Exercise IRC and capture the macOS application window
continue-on-error: true
timeout-minutes: 5
run: python3 packaging/macos/visual-smoke.py dist/ZoiteChat.app
- name: Upload screenshot and visual-check evidence
if: always()
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7
with:
name: ZoiteChat-macos-${{ matrix.arch }}-visual-check
path: dist/visual-check/
if-no-files-found: ignore
retention-days: 14
- name: Create artifact preserving the application signature
run: ditto -c -k --keepParent dist/ZoiteChat.app dist/ZoiteChat-${{ matrix.arch }}.zip
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7
with:
name: ZoiteChat-macos-${{ matrix.arch }}
path: dist/ZoiteChat-${{ matrix.arch }}.zip
if-no-files-found: error
- name: Upload build logs on failure
if: failure()
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7
with:
name: ZoiteChat-macos-${{ matrix.arch }}-logs
path: build-macos/meson-logs/
if-no-files-found: ignore
release:
name: Signed and notarized ${{ matrix.arch }} release
if: >-
(github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')) ||
(github.event_name == 'workflow_dispatch' && inputs.sign_and_notarize && github.ref == 'refs/heads/master')
environment: macos-release
strategy:
fail-fast: false
matrix:
include:
- runner: macos-15
arch: arm64
- runner: macos-15-intel
arch: x86_64
runs-on: ${{ matrix.runner }}
timeout-minutes: 180
env:
HOMEBREW_NO_AUTO_UPDATE: '1'
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Validate release settings before building
env:
MACOS_CERTIFICATE_P12_BASE64: ${{ secrets.MACOS_CERTIFICATE_P12_BASE64 }}
MACOS_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }}
MACOS_SIGNING_IDENTITY: ${{ vars.MACOS_SIGNING_IDENTITY || secrets.MACOS_SIGNING_IDENTITY }}
APPLE_API_KEY_P8: ${{ secrets.APPLE_API_KEY_P8 }}
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
APPLE_API_ISSUER_ID: ${{ secrets.APPLE_API_ISSUER_ID }}
run: python3 packaging/macos/release.py --check-config
- name: Install native dependencies
run: brew bundle --file packaging/macos/Brewfile
- name: Test packaging and release logic
run: python3 -m unittest discover -s packaging/macos/tests -v
- name: Build and test application
run: python3 packaging/macos/build.py --build-number '${{ github.run_number }}'
- name: Verify relocated startup
run: python3 packaging/macos/smoke.py dist/ZoiteChat.app
- name: Sign, notarize, staple and verify release
env:
MACOS_CERTIFICATE_P12_BASE64: ${{ secrets.MACOS_CERTIFICATE_P12_BASE64 }}
MACOS_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }}
MACOS_SIGNING_IDENTITY: ${{ vars.MACOS_SIGNING_IDENTITY || secrets.MACOS_SIGNING_IDENTITY }}
APPLE_API_KEY_P8: ${{ secrets.APPLE_API_KEY_P8 }}
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
APPLE_API_ISSUER_ID: ${{ secrets.APPLE_API_ISSUER_ID }}
run: >-
python3 packaging/macos/release.py
--app dist/ZoiteChat.app
--output dist/ZoiteChat-${{ matrix.arch }}-signed-notarized.zip
--diagnostics dist/notarization
--notary-timeout 60m
- name: Upload signed and notarized application
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7
with:
name: ZoiteChat-macos-${{ matrix.arch }}-signed-notarized
path: dist/ZoiteChat-${{ matrix.arch }}-signed-notarized.zip
if-no-files-found: error
- name: Upload notarization diagnostics
if: always()
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7
with:
name: ZoiteChat-macos-${{ matrix.arch }}-notarization-log
path: dist/notarization/*.json
if-no-files-found: ignore
- name: Upload build logs on failure
if: failure()
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7
with:
name: ZoiteChat-macos-${{ matrix.arch }}-release-build-logs
path: build-macos/meson-logs/
if-no-files-found: ignore
@@ -1,81 +0,0 @@
name: Manjaro Package Build
on:
push:
branches: [master, main]
pull_request:
branches: [master, main]
workflow_dispatch:
permissions:
contents: read
jobs:
manjaro-package:
runs-on: ubuntu-latest
container:
image: manjarolinux/base:latest
steps:
- name: Install packaging toolchain
run: |
set -euxo pipefail
pacman -Syu --noconfirm
pacman -S --noconfirm --needed \
base-devel \
git \
namcap \
meson \
ninja \
pkgconf \
glib2 \
glib2-devel \
gtk3 \
openssl \
dbus-glib \
libcanberra \
libayatana-appindicator \
luajit \
iso-codes \
lua \
perl \
python \
python-cffi \
pciutils
- name: Checkout repository
uses: actions/checkout@v5
with:
fetch-depth: 0
submodules: true
- name: Build package and generate .SRCINFO (non-root)
run: |
set -euxo pipefail
id -u builder >/dev/null 2>&1 || useradd -m builder
chown -R builder:builder "$GITHUB_WORKSPACE"
su builder -c "cd '$GITHUB_WORKSPACE/packaging/manjaro' && makepkg --noconfirm --syncdeps --cleanbuild"
su builder -c "cd '$GITHUB_WORKSPACE/packaging/manjaro' && makepkg --printsrcinfo > .SRCINFO"
- name: Lint PKGBUILD and package with namcap
run: |
set -euxo pipefail
cd "$GITHUB_WORKSPACE/packaging/manjaro"
namcap PKGBUILD
pkg_file="$(find . -maxdepth 1 -type f -name '*.pkg.tar.*' | head -n1)"
namcap "$pkg_file"
- name: Collect artifacts
run: |
set -euxo pipefail
mkdir -p artifacts
cp -v "$GITHUB_WORKSPACE"/packaging/manjaro/*.pkg.tar.* artifacts/
cp -v "$GITHUB_WORKSPACE"/packaging/manjaro/PKGBUILD artifacts/
cp -v "$GITHUB_WORKSPACE"/packaging/manjaro/.SRCINFO artifacts/
- name: Upload package artifacts
uses: actions/upload-artifact@v6
with:
name: zoitechat-manjaro-package
path: artifacts/*
if-no-files-found: error
+88 -6
View File
@@ -4,6 +4,7 @@ on:
push: push:
branches: branches:
- master - master
- 'release/**'
pull_request: pull_request:
branches: branches:
- master - master
@@ -25,9 +26,9 @@ jobs:
fail-fast: false fail-fast: false
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v7
- uses: actions/setup-python@v6 - uses: actions/setup-python@v7
with: with:
python-version: '3.14.3' python-version: '3.14.3'
architecture: ${{ matrix.arch }} architecture: ${{ matrix.arch }}
@@ -65,8 +66,32 @@ jobs:
Download-WithRetry -Url https://github.com/jrsoftware/issrc/releases/download/is-6_7_1/innosetup-6.7.1.exe -OutFile deps\innosetup-unicode.exe Download-WithRetry -Url https://github.com/jrsoftware/issrc/releases/download/is-6_7_1/innosetup-6.7.1.exe -OutFile deps\innosetup-unicode.exe
& deps\innosetup-unicode.exe /VERYSILENT | Out-Null & deps\innosetup-unicode.exe /VERYSILENT | Out-Null
Download-WithRetry -Url https://github.com/ZoiteChat/gvsbuild/releases/download/zoitechat-2.18.0-pre1/GTK3_Gvsbuild_zoitechat-2.18.0-pre1_${{ matrix.platform }}.7z -OutFile deps\gtk-${{ matrix.arch }}.7z Download-WithRetry -Url https://github.com/ZoiteChat/gvsbuild/releases/download/zoitechat-2.20.0-alpha.1/GTK3_Gvsbuild_zoitechat-2.20.0-alpha.1_x64.zip -OutFile deps\gtk-${{ matrix.arch }}.zip
& 7z.exe x deps\gtk-${{ matrix.arch }}.7z -oC:\gtk-build\gtk\x64\release Expand-Archive -LiteralPath deps\gtk-${{ matrix.arch }}.zip -DestinationPath C:\gtk-build\gtk\x64\release -Force
# The GTK3 archive omits librsvg. Add only its renderer, loader and
# license from the companion release, keeping the GTK3 DLLs intact.
Download-WithRetry -Url https://github.com/ZoiteChat/gvsbuild/releases/download/zoitechat-2.20.0-alpha.1/GTK4_Gvsbuild_zoitechat-2.20.0-alpha.1_x64.zip -OutFile deps\svg-runtime.zip
if ((Get-FileHash deps\svg-runtime.zip -Algorithm SHA256).Hash -ne 'fb657f9ebae3a6807defb15e66091d643a703ee66cb37488e3e11e5646f45adb') {
throw 'Unexpected SVG runtime archive checksum'
}
$svgArchive = [System.IO.Compression.ZipFile]::OpenRead((Resolve-Path deps\svg-runtime.zip))
try {
foreach ($name in @('bin/rsvg-2-2.dll', 'lib/gdk-pixbuf-2.0/2.10.0/loaders/pixbufloader_svg.dll', 'share/doc/librsvg/COPYING.LIB')) {
$entry = $svgArchive.GetEntry($name)
if ($null -eq $entry) { throw "Missing SVG runtime file: $name" }
$destination = Join-Path 'C:\gtk-build\gtk\x64\release' $name
New-Item -ItemType Directory -Path (Split-Path $destination) -Force | Out-Null
[System.IO.Compression.ZipFileExtensions]::ExtractToFile($entry, $destination, $true)
}
}
finally { $svgArchive.Dispose() }
# This loads the module now, catching missing DLLs before the build.
& C:\gtk-build\gtk\x64\release\bin\gdk-pixbuf-query-loaders.exe --update-cache
if ($LASTEXITCODE -ne 0) { throw 'Could not generate the pixbuf loader cache' }
if (-not (Select-String -LiteralPath 'C:\gtk-build\gtk\x64\release\lib\gdk-pixbuf-2.0\2.10.0\loaders.cache' -SimpleMatch '"svg"')) {
throw 'SVG loader could not be loaded with the GTK3 runtime'
}
Download-WithRetry -Url https://repo.msys2.org/mingw/x86_64/mingw-w64-x86_64-hicolor-icon-theme-0.18-1-any.pkg.tar.zst -OutFile deps\hicolor-icon-theme.pkg.tar.zst Download-WithRetry -Url https://repo.msys2.org/mingw/x86_64/mingw-w64-x86_64-hicolor-icon-theme-0.18-1-any.pkg.tar.zst -OutFile deps\hicolor-icon-theme.pkg.tar.zst
python -c "import tarfile,zstandard,pathlib;archive=pathlib.Path(r'deps\\hicolor-icon-theme.pkg.tar.zst');target=pathlib.Path(r'C:\\gtk-build\\gtk\\x64\\release');dctx=zstandard.ZstdDecompressor();f=archive.open('rb');reader=dctx.stream_reader(f);tf=tarfile.open(fileobj=reader,mode='r|');[tf.extract(m,path=target) for m in tf if m.name.startswith('mingw64/share/icons/hicolor/')];tf.close();reader.close();f.close()" python -c "import tarfile,zstandard,pathlib;archive=pathlib.Path(r'deps\\hicolor-icon-theme.pkg.tar.zst');target=pathlib.Path(r'C:\\gtk-build\\gtk\\x64\\release');dctx=zstandard.ZstdDecompressor();f=archive.open('rb');reader=dctx.stream_reader(f);tf=tarfile.open(fileobj=reader,mode='r|');[tf.extract(m,path=target) for m in tf if m.name.startswith('mingw64/share/icons/hicolor/')];tf.close();reader.close();f.close()"
@@ -113,6 +138,26 @@ jobs:
New-Item -Path $pyDir -Name "${{ matrix.platform }}" -ItemType Junction -Value $pyRoot | Out-Null New-Item -Path $pyDir -Name "${{ matrix.platform }}" -ItemType Junction -Value $pyRoot | Out-Null
} }
- uses: actions/setup-python@v7
with:
python-version: '3.8.10'
architecture: ${{ matrix.arch }}
- name: Prepare Python 3.8
run: |
python -m pip install --upgrade pip
python -m pip install cffi
$pyRoot = $env:pythonLocation
if (-not $pyRoot) { $pyRoot = & python -c "import sys; print(sys.prefix)" }
foreach ($pyDir in @("C:\gtk-build\python-3.8.10", "C:\gtk-build\python-3.8")) {
New-Item -Path $pyDir -ItemType Directory -Force | Out-Null
$target = Join-Path $pyDir "${{ matrix.platform }}"
if (Test-Path $target) { Remove-Item $target -Recurse -Force }
New-Item -Path $pyDir -Name "${{ matrix.platform }}" -ItemType Junction -Value $pyRoot | Out-Null
}
- name: Build - name: Build
run: | run: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\Tools\VsDevCmd.bat" call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\Tools\VsDevCmd.bat"
@@ -124,14 +169,51 @@ jobs:
exit /b 1 exit /b 1
) )
set "PYTHON38_DIR=C:\gtk-build\python-3.8\${{ matrix.platform }}"
if not exist "%PYTHON38_DIR%\libs\python38.lib" (
echo Missing %PYTHON38_DIR%\libs\python38.lib
dir "%PYTHON38_DIR%\libs"
exit /b 1
)
if not exist "%PYTHON38_DIR%\Lib\site-packages\_cffi_backend.cp38*.pyd" (
echo Missing %PYTHON38_DIR%\Lib\site-packages\_cffi_backend.cp38*.pyd
exit /b 1
)
set "LIB=%PYTHON_DIR%\libs;%LIB%" set "LIB=%PYTHON_DIR%\libs;%LIB%"
set "INCLUDE=%PYTHON_DIR%\include;%INCLUDE%" set "INCLUDE=%PYTHON_DIR%\include;%INCLUDE%"
powershell -NoProfile -ExecutionPolicy Bypass -Command "$archiveLib='C:\gtk-build\gtk\x64\release\lib\libarchive.lib'; if (-not (Test-Path $archiveLib)) { $archiveDll = Get-ChildItem 'C:\gtk-build\gtk\x64\release\bin\libarchive*.dll' | Select-Object -First 1; if ($archiveDll) { Push-Location 'C:\gtk-build\gtk\x64\release\lib'; & 'C:\gtk-build\gendef\gendef.exe' $archiveDll.FullName | Out-Null; $archiveDef = Get-ChildItem 'libarchive*.def' | Select-Object -First 1; if ($archiveDef) { & lib /def:$archiveDef.Name /machine:${{ matrix.platform }} /out:libarchive.lib | Out-Null }; Pop-Location } }" powershell -NoProfile -ExecutionPolicy Bypass -Command "$archiveLib='C:\gtk-build\gtk\x64\release\lib\libarchive.lib'; if (-not (Test-Path $archiveLib)) { $archiveDll = Get-ChildItem 'C:\gtk-build\gtk\x64\release\bin\libarchive*.dll' | Select-Object -First 1; if ($archiveDll) { Push-Location 'C:\gtk-build\gtk\x64\release\lib'; & 'C:\gtk-build\gendef\gendef.exe' $archiveDll.FullName | Out-Null; $archiveDef = Get-ChildItem 'libarchive*.def' | Select-Object -First 1; if ($archiveDef) { & lib /def:$archiveDef.Name /machine:${{ matrix.platform }} /out:libarchive.lib | Out-Null }; Pop-Location } }"
msbuild win32\zoitechat.sln /m /verbosity:minimal /p:Configuration=Release /p:Platform=${{ matrix.platform }} msbuild win32\zoitechat.sln /m /verbosity:minimal /p:Configuration=Release /p:Platform=${{ matrix.platform }}
if errorlevel 1 exit /b 1
if not exist "..\zoitechat-build\${{ matrix.platform }}\rel\plugins\hcpython38.dll" (
echo hcpython38.dll was not built
exit /b 1
)
if not exist "..\zoitechat-build\${{ matrix.platform }}\rel\_cffi_backend.cp38*.pyd" (
echo _cffi_backend for Python 3.8 was not staged for the installer
exit /b 1
)
shell: cmd shell: cmd
- name: Test emoji catalog, font coverage and picker
shell: cmd
run: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\Tools\VsDevCmd.bat"
set "PATH=%CD%\..\zoitechat-build\x64\rel;%PATH%"
set "GDK_PIXBUF_MODULE_FILE=%CD%\..\zoitechat-build\x64\rel\lib\gdk-pixbuf-2.0\2.10.0\loaders.cache"
set "ZOITECHAT_EMOJI_FONT=%CD%\..\zoitechat-build\x64\rel\share\fonts\zoitechat\NotoColorEmoji.ttf"
fc /b data\fonts\NotoColorEmoji.ttf "%ZOITECHAT_EMOJI_FONT%"
if errorlevel 1 exit /b 1
for %%T in (data font ui) do (
msbuild win32\emoji-tests.vcxproj /m /verbosity:minimal /p:Configuration=Release /p:Platform=x64 /p:EmojiTestKind=%%T
if errorlevel 1 exit /b 1
..\zoitechat-build\x64\tests\emoji-%%T-tests.exe --tap
if errorlevel 1 exit /b 1
)
- name: Preparing Artifacts - name: Preparing Artifacts
run: | run: |
move ..\zoitechat-build\${{ matrix.platform }}\ZoiteChat-*.exe .\ move ..\zoitechat-build\${{ matrix.platform }}\ZoiteChat-*.exe .\
@@ -140,7 +222,7 @@ jobs:
- name: Upload Installer - name: Upload Installer
id: upload_installer id: upload_installer
uses: actions/upload-artifact@v6 uses: actions/upload-artifact@v7
with: with:
name: Installer ${{ matrix.arch }} name: Installer ${{ matrix.arch }}
path: ZoiteChat-*.exe path: ZoiteChat-*.exe
@@ -154,7 +236,7 @@ jobs:
- name: Upload Build Files - name: Upload Build Files
id: upload_buildfiles id: upload_buildfiles
uses: actions/upload-artifact@v6 uses: actions/upload-artifact@v7
with: with:
name: Build Files ${{ matrix.arch }} name: Build Files ${{ matrix.arch }}
path: zoitechat-build path: zoitechat-build
+428
View File
@@ -0,0 +1,428 @@
name: Windows MSI/MSIX Build
on:
push:
branches:
- master
- 'release/**'
pull_request:
branches:
- master
workflow_dispatch:
jobs:
windows_msi_build:
runs-on: windows-2022
permissions:
contents: read
id-token: write
attestations: write
artifact-metadata: write
strategy:
matrix:
platform: [x64]
arch: [x64]
fail-fast: false
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v7
with:
python-version: '3.14.3'
architecture: ${{ matrix.arch }}
- name: Install Dependencies
shell: pwsh
run: |
New-Item -Name "deps" -ItemType "Directory" -Force | Out-Null
python -m pip install --upgrade pip
python -m pip install cffi
python -m pip install zstandard
$ProgressPreference = 'SilentlyContinue'
function Download-WithRetry {
param(
[string]$Url,
[string]$OutFile,
[int]$MaxAttempts = 5,
[int]$InitialDelaySeconds = 2
)
for ($attempt = 1; $attempt -le $MaxAttempts; $attempt++) {
try {
Invoke-WebRequest -Uri $Url -OutFile $OutFile -ErrorAction Stop
return
}
catch {
if ($attempt -eq $MaxAttempts) {
throw
}
Start-Sleep -Seconds ($InitialDelaySeconds * [math]::Pow(2, $attempt - 1))
}
}
}
# The existing solution includes installer.vcxproj, so Inno Setup is
# still required to build the unmodified solution. This workflow does
# not publish that EXE; it packages the same staged rel tree as MSI/MSIX.
Download-WithRetry -Url https://github.com/jrsoftware/issrc/releases/download/is-6_7_1/innosetup-6.7.1.exe -OutFile deps\innosetup-unicode.exe
& deps\innosetup-unicode.exe /VERYSILENT | Out-Null
# Use the same GTK runtime as the existing Windows EXE workflow.
Download-WithRetry -Url https://github.com/ZoiteChat/gvsbuild/releases/download/zoitechat-2.20.0-alpha.1/GTK3_Gvsbuild_zoitechat-2.20.0-alpha.1_x64.zip -OutFile deps\gtk-${{ matrix.arch }}.zip
Expand-Archive -LiteralPath deps\gtk-${{ matrix.arch }}.zip -DestinationPath C:\gtk-build\gtk\x64\release -Force
# The GTK3 bundle does not contain librsvg. Restore the renderer,
# pixbuf loader, and license from the matching GTK4 bundle. Do not
# replace the rest of the GTK3 runtime with GTK4 libraries.
Download-WithRetry -Url https://github.com/ZoiteChat/gvsbuild/releases/download/zoitechat-2.20.0-alpha.1/GTK4_Gvsbuild_zoitechat-2.20.0-alpha.1_x64.zip -OutFile deps\svg-runtime.zip
if ((Get-FileHash deps\svg-runtime.zip -Algorithm SHA256).Hash -ne 'fb657f9ebae3a6807defb15e66091d643a703ee66cb37488e3e11e5646f45adb') {
throw 'Unexpected SVG runtime archive checksum'
}
$svgArchive = [System.IO.Compression.ZipFile]::OpenRead((Resolve-Path deps\svg-runtime.zip))
try {
foreach ($name in @('bin/rsvg-2-2.dll', 'lib/gdk-pixbuf-2.0/2.10.0/loaders/pixbufloader_svg.dll', 'share/doc/librsvg/COPYING.LIB')) {
$entry = $svgArchive.GetEntry($name)
if ($null -eq $entry) { throw "Missing SVG runtime file: $name" }
$destination = Join-Path 'C:\gtk-build\gtk\x64\release' $name
New-Item -ItemType Directory -Path (Split-Path $destination) -Force | Out-Null
[System.IO.Compression.ZipFileExtensions]::ExtractToFile($entry, $destination, $true)
}
}
finally { $svgArchive.Dispose() }
# Ensure SVG remains loadable before compiling or packaging.
& C:\gtk-build\gtk\x64\release\bin\gdk-pixbuf-query-loaders.exe --update-cache
if ($LASTEXITCODE -ne 0) { throw 'Could not generate the pixbuf loader cache' }
if (-not (Select-String -LiteralPath 'C:\gtk-build\gtk\x64\release\lib\gdk-pixbuf-2.0\2.10.0\loaders.cache' -SimpleMatch '"svg"')) {
throw 'SVG loader could not be loaded with the GTK3 runtime'
}
Download-WithRetry -Url https://repo.msys2.org/mingw/x86_64/mingw-w64-x86_64-hicolor-icon-theme-0.18-1-any.pkg.tar.zst -OutFile deps\hicolor-icon-theme.pkg.tar.zst
python -c "import tarfile,zstandard,pathlib;archive=pathlib.Path(r'deps\\hicolor-icon-theme.pkg.tar.zst');target=pathlib.Path(r'C:\\gtk-build\\gtk\\x64\\release');dctx=zstandard.ZstdDecompressor();f=archive.open('rb');reader=dctx.stream_reader(f);tf=tarfile.open(fileobj=reader,mode='r|');[tf.extract(m,path=target) for m in tf if m.name.startswith('mingw64/share/icons/hicolor/')];tf.close();reader.close();f.close()"
Download-WithRetry -Url https://repo.msys2.org/mingw/x86_64/mingw-w64-x86_64-libarchive-3.8.6-1-any.pkg.tar.zst -OutFile deps\libarchive.pkg.tar.zst
python -c "import tarfile,zstandard,pathlib;archive=pathlib.Path(r'deps\\libarchive.pkg.tar.zst');target=pathlib.Path(r'C:\\gtk-build\\gtk\\x64\\release');dctx=zstandard.ZstdDecompressor();f=archive.open('rb');reader=dctx.stream_reader(f);tf=tarfile.open(fileobj=reader,mode='r|');[tf.extract(m,path=target) for m in tf if m.name.startswith(('mingw64/include/archive','mingw64/lib/libarchive','mingw64/bin/libarchive'))];tf.close();reader.close();f.close()"
if (Test-Path C:\gtk-build\gtk\x64\release\mingw64\share\icons\hicolor) {
New-Item -Path C:\gtk-build\gtk\x64\release\share\icons -ItemType Directory -Force | Out-Null
Copy-Item -Path C:\gtk-build\gtk\x64\release\mingw64\share\icons\hicolor -Destination C:\gtk-build\gtk\x64\release\share\icons\hicolor -Recurse -Force
}
if (Test-Path C:\gtk-build\gtk\x64\release\mingw64\include) {
New-Item -Path C:\gtk-build\gtk\x64\release\include -ItemType Directory -Force | Out-Null
Copy-Item -Path C:\gtk-build\gtk\x64\release\mingw64\include\archive* -Destination C:\gtk-build\gtk\x64\release\include -Recurse -Force
}
if (Test-Path C:\gtk-build\gtk\x64\release\mingw64\lib) {
New-Item -Path C:\gtk-build\gtk\x64\release\lib -ItemType Directory -Force | Out-Null
Copy-Item -Path C:\gtk-build\gtk\x64\release\mingw64\lib\libarchive* -Destination C:\gtk-build\gtk\x64\release\lib -Force
}
if (Test-Path C:\gtk-build\gtk\x64\release\mingw64\bin) {
New-Item -Path C:\gtk-build\gtk\x64\release\bin -ItemType Directory -Force | Out-Null
Copy-Item -Path C:\gtk-build\gtk\x64\release\mingw64\bin\libarchive*.dll -Destination C:\gtk-build\gtk\x64\release\bin -Force
}
if (Test-Path C:\gtk-build\gtk\x64\release\mingw64) {
Remove-Item -Path C:\gtk-build\gtk\x64\release\mingw64 -Recurse -Force
}
Download-WithRetry -Url https://github.com/ZoiteChat/gvsbuild/releases/download/zoitechat-2.18.0-pre4/gendef20260315.7z -OutFile deps\gendef.7z
& 7z.exe x deps\gendef.7z -oC:\gtk-build
Download-WithRetry -Url https://github.com/ZoiteChat/gvsbuild/releases/download/zoitechat-2.18.0-pre4/WinSparkle-20260315.7z -OutFile deps\WinSparkle.7z
& 7z.exe x deps\WinSparkle.7z -oC:\gtk-build\WinSparkle
Download-WithRetry -Url https://github.com/ZoiteChat/gvsbuild/releases/download/zoitechat-2.18.0-pre4/perl-5.42.0.1-${{ matrix.arch }}.7z -OutFile deps\perl-${{ matrix.arch }}.7z
& 7z.exe x deps\perl-${{ matrix.arch }}.7z -oC:\gtk-build\perl-5.42.0.1\${{ matrix.platform }}
$pyRoot = $env:pythonLocation
if (-not $pyRoot) { $pyRoot = & python -c "import sys; print(sys.prefix)" }
foreach ($pyDir in @("C:\gtk-build\python-3.14.3", "C:\gtk-build\python-3.14")) {
New-Item -Path $pyDir -ItemType Directory -Force | Out-Null
$target = Join-Path $pyDir "${{ matrix.platform }}"
if (Test-Path $target) { Remove-Item $target -Recurse -Force }
New-Item -Path $pyDir -Name "${{ matrix.platform }}" -ItemType Junction -Value $pyRoot | Out-Null
}
- uses: actions/setup-python@v7
with:
python-version: '3.8.10'
architecture: ${{ matrix.arch }}
- name: Prepare Python 3.8
shell: pwsh
run: |
python -m pip install --upgrade pip
python -m pip install cffi
$pyRoot = $env:pythonLocation
if (-not $pyRoot) { $pyRoot = & python -c "import sys; print(sys.prefix)" }
foreach ($pyDir in @("C:\gtk-build\python-3.8.10", "C:\gtk-build\python-3.8")) {
New-Item -Path $pyDir -ItemType Directory -Force | Out-Null
$target = Join-Path $pyDir "${{ matrix.platform }}"
if (Test-Path $target) { Remove-Item $target -Recurse -Force }
New-Item -Path $pyDir -Name "${{ matrix.platform }}" -ItemType Junction -Value $pyRoot | Out-Null
}
- name: Build
shell: cmd
run: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\Tools\VsDevCmd.bat"
set "PYTHON_DIR=C:\gtk-build\python-3.14.3\${{ matrix.platform }}"
if not exist "%PYTHON_DIR%\libs\python314.lib" (
echo Missing %PYTHON_DIR%\libs\python314.lib
dir "%PYTHON_DIR%\libs"
exit /b 1
)
set "PYTHON38_DIR=C:\gtk-build\python-3.8\${{ matrix.platform }}"
if not exist "%PYTHON38_DIR%\libs\python38.lib" (
echo Missing %PYTHON38_DIR%\libs\python38.lib
dir "%PYTHON38_DIR%\libs"
exit /b 1
)
if not exist "%PYTHON38_DIR%\Lib\site-packages\_cffi_backend.cp38*.pyd" (
echo Missing %PYTHON38_DIR%\Lib\site-packages\_cffi_backend.cp38*.pyd
exit /b 1
)
set "LIB=%PYTHON_DIR%\libs;%LIB%"
set "INCLUDE=%PYTHON_DIR%\include;%INCLUDE%"
powershell -NoProfile -ExecutionPolicy Bypass -Command "$archiveLib='C:\gtk-build\gtk\x64\release\lib\libarchive.lib'; if (-not (Test-Path $archiveLib)) { $archiveDll = Get-ChildItem 'C:\gtk-build\gtk\x64\release\bin\libarchive*.dll' | Select-Object -First 1; if ($archiveDll) { Push-Location 'C:\gtk-build\gtk\x64\release\lib'; & 'C:\gtk-build\gendef\gendef.exe' $archiveDll.FullName | Out-Null; $archiveDef = Get-ChildItem 'libarchive*.def' | Select-Object -First 1; if ($archiveDef) { & lib /def:$archiveDef.Name /machine:${{ matrix.platform }} /out:libarchive.lib | Out-Null }; Pop-Location } }"
msbuild win32\zoitechat.sln /m /verbosity:minimal /p:Configuration=Release /p:Platform=${{ matrix.platform }}
if errorlevel 1 exit /b 1
if not exist "..\zoitechat-build\${{ matrix.platform }}\rel\plugins\hcpython38.dll" (
echo hcpython38.dll was not built
exit /b 1
)
if not exist "..\zoitechat-build\${{ matrix.platform }}\rel\_cffi_backend.cp38*.pyd" (
echo _cffi_backend for Python 3.8 was not staged for the installer
exit /b 1
)
# Master added native emoji font staging and tests after the release
# branch split. Run these before packaging so MSI/MSIX use verified files.
- name: Test emoji catalog, font coverage and picker
shell: cmd
run: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\Tools\VsDevCmd.bat"
set "PATH=%CD%\..\zoitechat-build\x64\rel;%PATH%"
set "GDK_PIXBUF_MODULE_FILE=%CD%\..\zoitechat-build\x64\rel\lib\gdk-pixbuf-2.0\2.10.0\loaders.cache"
set "ZOITECHAT_EMOJI_FONT=%CD%\..\zoitechat-build\x64\rel\share\fonts\zoitechat\NotoColorEmoji.ttf"
fc /b data\fonts\NotoColorEmoji.ttf "%ZOITECHAT_EMOJI_FONT%"
if errorlevel 1 exit /b 1
for %%T in (data font ui) do (
msbuild win32\emoji-tests.vcxproj /m /verbosity:minimal /p:Configuration=Release /p:Platform=x64 /p:EmojiTestKind=%%T
if errorlevel 1 exit /b 1
..\zoitechat-build\x64\tests\emoji-%%T-tests.exe --tap
if errorlevel 1 exit /b 1
)
- name: Install WiX Toolset
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
# Keep this pinned. WiX v6 introduced OSMF terms and WiX v7 adds
# explicit EULA acceptance; do not silently accept new terms in CI.
dotnet tool install --tool-path .wix wix --version 5.0.2
if ($LASTEXITCODE -ne 0) { throw 'Failed to install WiX Toolset 5.0.2' }
& .\.wix\wix.exe --version
if ($LASTEXITCODE -ne 0) { throw 'WiX Toolset could not be executed' }
- name: Build MSI
id: build_msi
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
$rawVersion = (Get-Content VERSION -Raw).Trim()
if ($rawVersion -notmatch '^(\d+)\.(\d+)\.(\d+)') {
throw "VERSION '$rawVersion' does not begin with an MSI-compatible major.minor.patch version"
}
# Keep the full development version in the filename, while MSI's
# ProductVersion is numeric (e.g. 2.20.0-dev -> 2.20.0).
$msiVersion = "$($Matches[1]).$($Matches[2]).$($Matches[3])"
$safeVersion = $rawVersion -replace '[^0-9A-Za-z._-]', '-'
$sourceDir = (Resolve-Path "..\zoitechat-build\${{ matrix.platform }}\rel").Path
$outputPath = Join-Path $PWD "ZoiteChat-${safeVersion}_${{ matrix.arch }}.msi"
if (-not (Test-Path (Join-Path $sourceDir 'zoitechat.exe'))) {
throw "Staged ZoiteChat executable is missing from $sourceDir"
}
& .\.wix\wix.exe build win32\wix\zoitechat.wxs `
-arch x64 `
-d "SourceDir=$sourceDir" `
-d "ProductVersion=$msiVersion" `
-out $outputPath
if ($LASTEXITCODE -ne 0) { throw 'WiX MSI build failed' }
if (-not (Test-Path $outputPath)) {
throw "Expected MSI was not created: $outputPath"
}
"path=$outputPath" >> $env:GITHUB_OUTPUT
"filename=$(Split-Path $outputPath -Leaf)" >> $env:GITHUB_OUTPUT
- name: Test MSI administrative extraction
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
$msi = '${{ steps.build_msi.outputs.path }}'
$adminImage = Join-Path $PWD 'msi-admin-image'
New-Item -ItemType Directory -Path $adminImage -Force | Out-Null
$arguments = "/a `"$msi`" /qn TARGETDIR=`"$adminImage`""
$process = Start-Process -FilePath msiexec.exe -ArgumentList $arguments -Wait -PassThru
if ($process.ExitCode -ne 0) {
throw "MSI administrative extraction failed with exit code $($process.ExitCode)"
}
foreach ($requiredFile in @(
'zoitechat.exe',
'hclua.dll',
'hcupd.dll',
'WinSparkle.dll',
'hcchecksum.dll',
'hcexec.dll',
'hcfishlim.dll',
'hcsysinfo.dll',
'system.png',
'hcperl.dll',
'hcpython3.dll',
'hcpython38.dll',
'NotoColorEmoji.ttf',
'OFL.txt'
)) {
$match = Get-ChildItem -Path $adminImage -Filter $requiredFile -File -Recurse | Select-Object -First 1
if ($null -eq $match) {
throw "Administrative image does not contain required normal-install file: $requiredFile"
}
}
foreach ($requiredPattern in @('_cffi_backend.cp314*.pyd', '_cffi_backend.cp38*.pyd')) {
$match = Get-ChildItem -Path $adminImage -Filter $requiredPattern -File -Recurse | Select-Object -First 1
if ($null -eq $match) {
throw "Administrative image does not contain required Python support file: $requiredPattern"
}
}
foreach ($requiredPythonFile in @('zoitechat.py', '_zoitechat.py', 'hexchat.py', 'xchat.py')) {
$match = Get-ChildItem -Path $adminImage -Filter $requiredPythonFile -File -Recurse |
Where-Object { $_.Directory.Name -eq 'python' } |
Select-Object -First 1
if ($null -eq $match) {
throw "Administrative image does not contain required Python bridge file: $requiredPythonFile"
}
}
foreach ($excludedFile in @(
'portable-mode',
'zoitechat-text.exe'
)) {
$match = Get-ChildItem -Path $adminImage -Filter $excludedFile -File -Recurse | Select-Object -First 1
if ($null -ne $match) {
throw "MSI incorrectly contains Inno custom/portable-only file: $excludedFile"
}
}
- name: Build MSIX
id: build_msix
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
$rawVersion = (Get-Content VERSION -Raw).Trim()
if ($rawVersion -notmatch '^(\d+)\.(\d+)\.(\d+)') {
throw "VERSION '$rawVersion' does not begin with a Store-compatible major.minor.build version"
}
$versionParts = @(
[int]$Matches[1],
[int]$Matches[2],
[int]$Matches[3]
)
if ($versionParts[0] -eq 0) {
throw 'The Microsoft Store requires a non-zero major package version.'
}
foreach ($part in $versionParts) {
if ($part -lt 0 -or $part -gt 65535) {
throw "MSIX version component '$part' is outside the Store-supported 0-65535 range."
}
}
# Partner Center reserves the fourth field as 0. A -dev version
# is packaged as a valid 2.20.0.0, without changing VERSION.
$packageVersion = "$($versionParts[0]).$($versionParts[1]).$($versionParts[2]).0"
$safeVersion = $rawVersion -replace '[^0-9A-Za-z._-]', '-'
$sourceDir = (Resolve-Path "..\zoitechat-build\${{ matrix.platform }}\rel").Path
$outputPath = Join-Path $PWD "ZoiteChat-${safeVersion}_${{ matrix.arch }}.msix"
# Reserved ZoiteChat identity from the release branch. Adjust only
# if the Product identity page in Partner Center changes.
$identityName = 'deependtilde.ZoiteChat'
$publisher = 'CN=6C7766BB-6AC6-488B-A09E-E3F8E68E63E4'
$publisherDisplayName = 'deepend_tilde'
$artifactName = 'MSIX Store Package ${{ matrix.arch }}'
Write-Host "Building Store MSIX for identity: $identityName"
& .\win32\msix\build-msix.ps1 `
-SourceDir $sourceDir `
-OutputPath $outputPath `
-PackageVersion $packageVersion `
-IdentityName $identityName `
-Publisher $publisher `
-PublisherDisplayName $publisherDisplayName `
-SelfSign
if ($LASTEXITCODE -ne 0) {
throw 'MSIX build script failed'
}
if (-not (Test-Path $outputPath)) {
throw "Expected MSIX was not created: $outputPath"
}
"path=$outputPath" >> $env:GITHUB_OUTPUT
"filename=$(Split-Path $outputPath -Leaf)" >> $env:GITHUB_OUTPUT
"package_version=$packageVersion" >> $env:GITHUB_OUTPUT
"store_ready=true" >> $env:GITHUB_OUTPUT
"artifact_name=$artifactName" >> $env:GITHUB_OUTPUT
- name: Upload MSI Installer
id: upload_msi
uses: actions/upload-artifact@v7
with:
name: MSI Installer ${{ matrix.arch }}
path: ${{ steps.build_msi.outputs.path }}
if-no-files-found: error
- name: Attest MSI Installer (Artifact Attestation)
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }}
uses: actions/attest-build-provenance@v3
with:
subject-name: MSI Installer ${{ matrix.arch }}
subject-digest: sha256:${{ steps.upload_msi.outputs.artifact-digest }}
- name: Upload MSIX Package
id: upload_msix
uses: actions/upload-artifact@v7
with:
name: ${{ steps.build_msix.outputs.artifact_name }}
path: ${{ steps.build_msix.outputs.path }}
if-no-files-found: error
- name: Attest MSIX Package (Artifact Attestation)
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }}
uses: actions/attest-build-provenance@v3
with:
subject-name: ${{ steps.build_msix.outputs.artifact_name }}
subject-digest: sha256:${{ steps.upload_msix.outputs.artifact-digest }}
+6
View File
@@ -27,3 +27,9 @@ osx/.ZoiteChat.app
*.dmg *.dmg
*.VC.db *.VC.db
*.VC.opendb *.VC.opendb
# Native macOS packaging output
/build-macos*/
/dist/
/packaging/macos/__pycache__/
/packaging/macos/tests/__pycache__/
+7
View File
@@ -0,0 +1,7 @@
version: 2
sphinx:
configuration: 'docs/conf.py'
build:
os: 'ubuntu-22.04'
tools:
python: '3.11'
+45 -24
View File
@@ -1,12 +1,12 @@
GNU GENERAL PUBLIC LICENSE GNU GENERAL PUBLIC LICENSE
Version 2, June 1991 Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc. Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed. of this license document, but changing it is not allowed.
Preamble Preamble
The licenses for most software are designed to take away your The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public freedom to share and change it. By contrast, the GNU General Public
@@ -15,7 +15,7 @@ software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to the GNU Lesser General Public License instead.) You can apply it to
your programs, too. your programs, too.
When we speak of free software, we are referring to freedom, not When we speak of free software, we are referring to freedom, not
@@ -55,8 +55,8 @@ patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and The precise terms and conditions for copying, distribution and
modification follow. modification follow.
GNU GENERAL PUBLIC LICENSE GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains 0. This License applies to any program or other work which contains
@@ -110,7 +110,7 @@ above, provided that you also meet all of these conditions:
License. (Exception: if the Program itself is interactive but License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on does not normally print such an announcement, your work based on
the Program is not required to print an announcement.) the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program, identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in and can be reasonably considered independent and separate works in
@@ -168,7 +168,7 @@ access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not distribution of the source code, even though third parties are not
compelled to copy the source along with the object code. compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program 4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is otherwise to copy, modify, sublicense or distribute the Program is
@@ -225,7 +225,7 @@ impose that choice.
This section is intended to make thoroughly clear what is believed to This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License. be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in 8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License original copyright holder who places the Program under this License
@@ -255,7 +255,7 @@ make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally. of promoting the sharing and reuse of software generally.
NO WARRANTY NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
@@ -277,9 +277,9 @@ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES. POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it possible use to the public, the best way to achieve this is to make it
@@ -291,7 +291,7 @@ convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found. the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.> <one line to give the program's name and a brief idea of what it does.>
Copyright (C) 19yy <name of author> Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -303,17 +303,15 @@ the "copyright" line and a pointer to where the full notice is found.
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License along
along with this program; if not, write to the Free Software with this program; if not, see <https://www.gnu.org/licenses/>.
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Also add information on how to contact you by electronic and paper mail. Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this If the program is interactive, make it output a short notice like this
when it starts in an interactive mode: when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details. under certain conditions; type `show c' for details.
@@ -330,11 +328,34 @@ necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker. `Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989 <signature of Moe Ghoul>, 1 April 1989
Ty Coon, President of Vice Moe Ghoul, President of Vice
This General Public License does not permit incorporating your program into This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. Public License instead of this License.
======================================================================
ADDITIONAL LICENSE NOTICE
======================================================================
ZoiteChat source code:
GPL version 2
See Above.
Unicode Emoji and CLDR data:
Unicode License v3
data/emoji/LICENSE-UNICODE.txt
gemoji shortcode data:
MIT License
Copyright (c) 2019 GitHub, Inc.
data/emoji/LICENSE-GEMOJI.txt
Noto Color Emoji:
SIL Open Font License 1.1
data/fonts/OFL.txt
+1
View File
@@ -0,0 +1 @@
2.20.0-dev
+82
View File
@@ -1,7 +1,89 @@
ZoiteChat ChangeLog ZoiteChat ChangeLog
================= =================
2.19.0 (2026-07-28)
-------------------
- Added Python 3.8 compatibility for older Windows releases.
- Added on-demand SASL password prompts, including renewed prompts after reconnecting.
- Corrected NickServ authentication to use the decrypted password for the active network.
- Made system theme matching the default and polished GTK3 theme integration.
- Applied more settings changes immediately, reducing the need to restart ZoiteChat.
- Added descriptive tooltips to the color picker choices and refined default text-event colors.
- Stabilized channel tree font handling across palette and theme updates.
- Corrected the chat scrollbar increment used by its arrow buttons.
- Improved Windows URL launching through the native shell.
- Restored expected minimize behavior when clicking ZoiteChat on the Windows taskbar.
2.18.3 (2026-06-29)
-------------------
- Added IRCv3 support for message tags, echo-message, typing notifications, and replies.
- Restored the accept-invalid-cert TLS bypass behavior.
- Replaced legacy IPv4 APIs and added support for OpenSSL 4 APIs.
- Snapshot Python hooks during plugin unload for safer plugin shutdown.
- Fixed Windows GtkStatusIcon tray menu popup behavior.
- Fixed self-echoed private messages routing to the target tab.
- Fixed the lag ping timeout window.
2.18.2 (2026-06-20)
-------------------
- Improved security and connection handling with stronger TLS defaults,
safer certificate/hostname validation, FiSHLiM OpenSSL fixes, keyring support,
and getaddrinfo-based DCC lookups.
- Expanded FiSHLiM usability with a GTK key manager, context-menu access, and clearer
channel/private-message key handling.
- Improved keyboard and navigation behavior with customizable keybinds, reset support,
channel-name switching, /server -noproxy, and configurable stale-link ping checks.
- Refined the GTK interface with native file chooser dialogs, topic layout preferences,
timestamp/date hover tooltips, better restore-down relayout, text scroll speed controls,
and aligned network meters.
- Added privacy and display options, including hiding join/part hostmasks and improved
topic/multiline mode layout controls.
- Fixed several platform/UI edge cases, including tray restore from iconified state, DBus unload
handling, GTK3 theme refresh/saving, and tab switcher compatibility.
2.18.1 (2026-05-21)
-------------------
- Migrated D-Bus handling to GDBus.
- Enabled mouse wheel channel switching by default and consumed handled tab wheel events.
- Switched main panes to native GTK scrollbars.
- Tightened userlist button and meter layout.
- Made the native Windows file chooser modal.
- Set the Windows AppUserModelID before GTK startup.
- Updated Windows CI to use the new GTK3 bundle zip.
- Updated Windows installer architecture checks to use x64-compatible detection.
- Bumped the Flatpak Perl runtime to 5.42.2.
- Fixed palette color reads to preserve base GTK state.
- Fixed auto-replace whole-word matching.
- Fixed checksum file stream cleanup.
- Fixed byte handling in the Python console.
- Guarded GTK drag-and-drop handlers against null windows.
- Fixed size_t and integer handling issues.
- Removed the Winamp plugin.
2.18.0 (2026-04-20)
-------------------
- Added optional close buttons on tabs.
- Added Ctrl+W to close tabs and Ctrl+Shift+T to reopen recently closed tabs.
- Expanded theme import support with .hct support in colors.conf import.
- Added pevent import support from .hct theme files and improved import result messaging.
- Expanded palette and selection CSS styling for better theme consistency.
- Auto-sizes the topic bar to wrapped text for better multi-line topic handling.
- Refined command-character parsing when pasting path-like text.
- Tightened topic link detection and opening rules.
- Added Public Suffix List validation for host links to reduce bad link matches.
- Improved channel tree behavior by ignoring collapsed parent selections.
- Sanitized the Linux open environment for AppImage builds and added safer fallback handling.
- Labeled Windows installer runtimes with exact versions.
- Fixed the notification icon and corrected the Flatpak screenshot asset.
- Improved AppStream metainfo validation.
2.18.0~pre6 (2026-03-30) 2.18.0~pre6 (2026-03-30)
------------------------
- Applied app theme CSS to the menubar consistently across the app. - Applied app theme CSS to the menubar consistently across the app.
- Restored horizontal separator lines in menus. - Restored horizontal separator lines in menus.
+39
View File
@@ -0,0 +1,39 @@
UNICODE LICENSE V3
COPYRIGHT AND PERMISSION NOTICE
Copyright © 1991-2026 Unicode, Inc.
NOTICE TO USER: Carefully read the following legal agreement. BY
DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR
SOFTWARE, YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE
TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT
DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE.
Permission is hereby granted, free of charge, to any person obtaining a
copy of data files and any associated documentation (the "Data Files") or
software and any associated documentation (the "Software") to deal in the
Data Files or Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, and/or sell
copies of the Data Files or Software, and to permit persons to whom the
Data Files or Software are furnished to do so, provided that either (a)
this copyright and permission notice appear with all copies of the Data
Files or Software, or (b) this copyright and permission notice appear in
associated Documentation.
THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
THIRD PARTY RIGHTS.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE
BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES,
OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA
FILES OR SOFTWARE.
Except as contained in this notice, the name of a copyright holder shall
not be used in advertising or otherwise to promote the sale, use or other
dealings in these Data Files or Software without prior written
authorization of the copyright holder.
+30
View File
@@ -0,0 +1,30 @@
# Bundled emoji data
These are pinned copies of the upstream data files that
`tools/gen-emoji-data.py` compiles into ZoiteChat's emoji picker catalog
at build time. Bundling them means every ZoiteChat package exposes the
same emoji, names, categories and search keywords, independent of the
GTK runtime version or the emoji data a distribution installs under
`/usr/share/gtk-3.0/emoji`.
Nothing here affects what is sent over IRC: the picker inserts plain
Unicode sequences. Official builds register the bundled Noto Color Emoji
font privately for consistent rendering, while preserving platform font
fallback if registration is unavailable.
| File | Source | Version |
| ---- | ------ | ------- |
| `emoji-test.txt` | [Unicode emoji data](https://unicode.org/Public/emoji/17.0/emoji-test.txt) (mirrored at [unicode-org/unicodetools](https://github.com/unicode-org/unicodetools/blob/main/unicodetools/data/emoji/17.0/emoji-test.txt)) | Emoji 17.0 |
| `cldr-annotations-en.xml` | [unicode-org/cldr](https://github.com/unicode-org/cldr/blob/release-48/common/annotations/en.xml) `common/annotations/en.xml` | CLDR release 48 |
| `cldr-annotations-derived-en.xml` | [unicode-org/cldr](https://github.com/unicode-org/cldr/blob/release-48/common/annotationsDerived/en.xml) `common/annotationsDerived/en.xml` | CLDR release 48 |
| `gemoji.json` | [github/gemoji](https://github.com/github/gemoji/blob/v4.1.0/db/emoji.json) `db/emoji.json` | v4.1.0 |
The pinned versions are recorded in `VERSIONS`.
## Licenses
- `emoji-test.txt`, `cldr-annotations-en.xml`,
`cldr-annotations-derived-en.xml`: © Unicode, Inc., distributed under
the [Unicode License v3](https://www.unicode.org/license.txt).
- `gemoji.json`: from the gemoji project, MIT licensed,
© GitHub, Inc.
+6
View File
@@ -0,0 +1,6 @@
# Pinned versions of the emoji source data in this directory.
# Keep in sync with the files themselves; tools/gen-emoji-data.py
# verifies unicode-emoji against the emoji-test.txt header.
unicode-emoji=17.0
cldr=48
gemoji=4.1.0
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+23479
View File
File diff suppressed because it is too large. Load diff
Binary file not shown.
+91
View File
@@ -0,0 +1,91 @@
Copyright 2013 Google LLC
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
https://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) shall not be
used to promote, endorse or advertise any Modified Version, except to
acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s)
or with their explicit written permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
+49
View File
@@ -0,0 +1,49 @@
# Bundled emoji font
`NotoColorEmoji.ttf` reports Noto Color Emoji version 2.051 and covers
Unicode Emoji 17.0. It is vendored from Debian's
`fonts-noto-color-emoji` 2.051 package, built from the corresponding
Google Noto Emoji release.
By default, GTK builds install this exact file, including Windows
(installer and portable builds), AppImage, Flatpak, and native Meson/RPM.
Meson packagers can set `-Dinstall-emoji-font=false` to omit the bundled
font and its installed notices and use system emoji fonts instead. An
explicit `ZOITECHAT_EMOJI_FONT` path still enables the private font map
in such builds. With the bundled font, it is registered privately as
`ZoiteChat Emoji`; an older system Noto font cannot replace it. No font
is installed globally on Windows.
Chat windows and the emoji picker share this map. Fontconfig still loads
system fonts and configuration for ordinary text, and the configured text
font, size and style remain in effect. In particular, plain digits, `#`
and `*` are not assigned emoji metrics.
Windows uses the same FreeType backend rather than GDI registration or an
OS-version-specific picker. The dependency bundle already ships FreeType,
Fontconfig and PangoFT2. This rendering path does not require Windows' native
color-font support. Actual Windows 7 execution still needs testing on that OS.
Meson requires PangoFT2 >= 1.44 and Fontconfig >= 2.13. Normal installations
load `share/fonts/zoitechat/NotoColorEmoji.ttf` beneath the configured data
directory; Windows resolves it relative to the executable. AppImage sets
`ZOITECHAT_EMOJI_FONT` to its own copy. For an uninstalled development build:
```sh
ZOITECHAT_EMOJI_FONT="$PWD/data/fonts/NotoColorEmoji.ttf" build/src/fe-gtk/zoitechat
```
An unexpectedly missing or unloadable bundled font logs a warning and retains
system-font fallback. When installation is disabled deliberately, fallback is
quiet unless an explicit `ZOITECHAT_EMOJI_FONT` override is invalid.
CI verifies the installed copy and tests catalog/font coverage; AppImage and
Windows also run the interactive picker regression tests. Native themes,
DPI and different Pango/Cairo versions can still affect pixel-level output.
Upstream release: https://github.com/googlefonts/noto-emoji/releases/tag/v2.051
Vendored file SHA-256:
`dac5c27651082d6c53dab5081f50d2022ddf6877d730fa919cf6a4fc2af22de0`
The font is distributed under the SIL Open Font License 1.1. See
`OFL.txt`.
+15
View File
@@ -0,0 +1,15 @@
emoji_font_dir = join_paths(get_option('datadir'), 'fonts', 'zoitechat')
emoji_font_doc_dir = join_paths(get_option('datadir'), 'doc', 'zoitechat', 'fonts')
if get_option('install-emoji-font')
install_data(
'NotoColorEmoji.ttf',
install_dir: emoji_font_dir,
)
install_data(
'OFL.txt',
'README.md',
install_dir: emoji_font_doc_dir,
)
endif
-16
View File
@@ -1,16 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
version="1.1"
width="0.213333in" height="0.213333in"
viewBox="0 0 64 64">
<defs>
</defs>
<image id="raster0"
x="0"
y="0"
width="64"
height="64"
opacity="1.000000"
href="data:image/png;base64,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" />
</svg>

Before

Width:  |  Height:  |  Size: 4.8 KiB

-16
View File
@@ -1,16 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
version="1.1"
width="0.213333in" height="0.213333in"
viewBox="0 0 64 64">
<defs>
</defs>
<image id="raster0"
x="0"
y="0"
width="64"
height="64"
opacity="1.000000"
href="data:image/png;base64,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" />
</svg>

Before

Width:  |  Height:  |  Size: 4.8 KiB

+45 -15
View File
@@ -1,16 +1,46 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="900px" height="900px" viewBox="0 0 900 900" version="1.1">
xmlns:svg="http://www.w3.org/2000/svg" <defs>
version="1.1" <linearGradient id="linear0" gradientUnits="userSpaceOnUse" x1="14.94" y1="500" x2="893.400024" y2="500" gradientTransform="matrix(0.990818,0,0,0.899663,0,0.168452)">
width="0.213333in" height="0.213333in" <stop offset="0" style="stop-color:rgb(0.392157%,63.137255%,98.431373%);stop-opacity:1;"/>
viewBox="0 0 64 64"> <stop offset="1" style="stop-color:rgb(0%,27.843137%,73.72549%);stop-opacity:1;"/>
<defs> </linearGradient>
</defs> <linearGradient id="linear1" gradientUnits="userSpaceOnUse" x1="14.94" y1="533.090027" x2="823" y2="533.090027" gradientTransform="matrix(0.990818,0,0,0.899663,0,0.168452)">
<image id="raster0" <stop offset="0" style="stop-color:rgb(0%,27.843137%,73.72549%);stop-opacity:1;"/>
x="0" <stop offset="1" style="stop-color:rgb(0.392157%,63.137255%,98.431373%);stop-opacity:1;"/>
y="0" </linearGradient>
width="64" <filter id="alpha" filterUnits="objectBoundingBox" x="0%" y="0%" width="100%" height="100%">
height="64" <feColorMatrix type="matrix" in="SourceGraphic" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"/>
opacity="1.000000" </filter>
href="data:image/png;base64,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" /> <mask id="mask0">
<g filter="url(#alpha)">
<rect x="0" y="0" width="900" height="900" style="fill:rgb(0%,0%,0%);fill-opacity:0.301961;stroke:none;"/>
</g>
</mask>
<clipPath id="clip1">
<rect x="0" y="0" width="900" height="900"/>
</clipPath>
<g id="surface6" clip-path="url(#clip1)">
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;" d="M 807.664062 446.90625 C 807.664062 254.1875 635.601562 97.953125 423.355469 97.953125 C 299.296875 97.953125 188.988281 151.347656 118.71875 234.1875 C 185.085938 129.585938 309.460938 59.160156 451.960938 59.160156 C 664.214844 59.160156 836.269531 215.394531 836.269531 408.113281 C 836.269531 488.183594 806.546875 561.945312 756.597656 620.828125 C 789.078125 569.628906 807.664062 510.242188 807.664062 446.90625 Z M 807.664062 446.90625 "/>
</g>
<mask id="mask1">
<g filter="url(#alpha)">
<rect x="0" y="0" width="900" height="900" style="fill:rgb(0%,0%,0%);fill-opacity:0.501961;stroke:none;"/>
</g>
</mask>
<image id="image9" width="314" height="273" xlink:href="data:image/png;base64,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 truncated
<clipPath id="clip2">
<rect x="0" y="0" width="900" height="900"/>
</clipPath>
<g id="surface10" clip-path="url(#clip2)">
<use xlink:href="#image9" transform="matrix(1.581172,0,0,1.435704,208.067513,213.172682)"/>
</g>
</defs>
<g id="surface2">
<path style=" stroke:none;fill-rule:nonzero;fill:url(#linear0);" d="M 207.476562 790.304688 C 214.847656 775.902344 220.566406 761.195312 224.71875 746.339844 C 98.894531 677.074219 14.804688 551.554688 14.804688 408.183594 C 14.804688 189.945312 209.648438 13.023438 450 13.023438 C 690.351562 13.023438 885.195312 189.945312 885.195312 408.183594 C 885.195312 626.425781 690.351562 803.34375 450 803.34375 C 417.175781 803.34375 385.199219 800.023438 354.457031 793.761719 C 297.789062 857.851562 209.816406 892.078125 120.78125 886.34375 C 156.996094 862.296875 187.226562 829.847656 207.476562 790.296875 Z M 207.476562 790.304688 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:url(#linear1);" d="M 207.476562 790.304688 C 214.820312 775.984375 220.496094 761.347656 224.648438 746.582031 C 98.875 677.453125 14.804688 552.058594 14.804688 408.824219 C 14.804688 266.605469 97.683594 141.980469 221.972656 72.5625 C 178.703125 133.902344 153.617188 206.828125 153.617188 285.074219 C 153.617188 502.964844 348.144531 679.59375 588.109375 679.59375 C 671.449219 679.59375 749.296875 658.273438 815.433594 621.332031 C 738.210938 730.792969 603.082031 803.34375 449.296875 803.34375 C 416.679688 803.34375 384.914062 800.058594 354.355469 793.871094 C 297.691406 857.898438 209.757812 892.078125 120.769531 886.34375 C 156.984375 862.296875 187.214844 829.847656 207.46875 790.296875 Z M 207.476562 790.304688 "/>
<use xlink:href="#surface6" mask="url(#mask0)"/>
<use xlink:href="#surface10" mask="url(#mask1)"/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;" d="M 677.402344 225.28125 L 259.328125 225.28125 L 220.765625 309.644531 L 467.875 309.644531 L 220.765625 579.972656 L 638.898438 579.972656 L 677.402344 495.550781 L 430.363281 495.550781 L 677.402344 225.273438 Z M 677.402344 225.28125 "/>
</g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 14 KiB

+1
View File
@@ -3,6 +3,7 @@ if get_option('plugin')
endif endif
if get_option('gtk-frontend') if get_option('gtk-frontend')
subdir('fonts')
subdir('icons') subdir('icons')
subdir('misc') subdir('misc')
subdir('man') subdir('man')
+13 -6
View File
@@ -2,6 +2,7 @@ appdir = join_paths(get_option('datadir'), 'applications')
metainfodir = join_paths(get_option('datadir'), 'metainfo') metainfodir = join_paths(get_option('datadir'), 'metainfo')
mimedir = join_paths(get_option('datadir'), 'mime', 'packages') mimedir = join_paths(get_option('datadir'), 'mime', 'packages')
desktop_utils = find_program('desktop-file-validate', required: false) desktop_utils = find_program('desktop-file-validate', required: false)
appstreamcli = find_program('appstreamcli', required: false)
if get_option('gtk-frontend') if get_option('gtk-frontend')
if get_option('install-appdata') if get_option('install-appdata')
@@ -13,16 +14,15 @@ if get_option('gtk-frontend')
install_dir: metainfodir install_dir: metainfodir
) )
appstream_util = find_program('appstream-util', required: false) if appstreamcli.found()
if appstream_util.found() test('Validate net.zoite.Zoitechat.appdata.xml', appstreamcli,
test('Validate net.zoite.Zoitechat.appdata.xml', appstream_util, args: ['validate', zoitechat_appdata]
args: ['validate-relax', zoitechat_appdata]
) )
endif endif
endif endif
desktop_conf = configuration_data() desktop_conf = configuration_data()
if dbus_glib_dep.found() if dbus_dep.found()
desktop_conf.set('exec_command', 'zoitechat --existing %U') desktop_conf.set('exec_command', 'zoitechat --existing %U')
else else
desktop_conf.set('exec_command', 'zoitechat %U') desktop_conf.set('exec_command', 'zoitechat %U')
@@ -97,11 +97,18 @@ if get_option('plugin')
conf.set('SUMMARY', metainfo[1]) conf.set('SUMMARY', metainfo[1])
conf.set('LICENSE', metainfo[2]) conf.set('LICENSE', metainfo[2])
configure_file( plugin_appdata = configure_file(
input: 'net.zoite.Zoitechat.Plugin.metainfo.xml.in', input: 'net.zoite.Zoitechat.Plugin.metainfo.xml.in',
output: 'net.zoite.Zoitechat.Plugin.@0@.metainfo.xml'.format(name), output: 'net.zoite.Zoitechat.Plugin.@0@.metainfo.xml'.format(name),
configuration: conf, configuration: conf,
install_dir: get_option('install-plugin-metainfo') ? metainfodir : '', install_dir: get_option('install-plugin-metainfo') ? metainfodir : '',
) )
if appstreamcli.found()
test('Validate net.zoite.Zoitechat.Plugin.@0@.metainfo.xml'.format(name), appstreamcli,
args: ['validate', plugin_appdata]
)
endif
endforeach endforeach
endif endif
@@ -4,7 +4,7 @@
<extends>net.zoite.Zoitechat</extends> <extends>net.zoite.Zoitechat</extends>
<name>@NAME@ Plugin</name> <name>@NAME@ Plugin</name>
<summary>@SUMMARY@</summary> <summary>@SUMMARY@</summary>
<url type="homepage">https://zoitechat.zoite.net/</url> <url type="homepage">https://zoitechat.org/</url>
<project_license>@LICENSE@</project_license> <project_license>@LICENSE@</project_license>
<metadata_license>CC0-1.0</metadata_license> <metadata_license>CC0-1.0</metadata_license>
<update_contact>deepend_AT_zoite.net</update_contact> <update_contact>deepend_AT_zoite.net</update_contact>
+123 -11
View File
@@ -16,12 +16,12 @@
<p>ZoiteChat is an easy to use yet extensible IRC Client based on Hexchat. It allows you to securely join multiple networks and talk to users privately or in channels using a customizable interface. You can even transfer files.</p> <p>ZoiteChat is an easy to use yet extensible IRC Client based on Hexchat. It allows you to securely join multiple networks and talk to users privately or in channels using a customizable interface. You can even transfer files.</p>
<p>ZoiteChat supports features such as: DCC, SASL, proxies, spellcheck, alerts, logging, custom themes, and Python/Perl scripts.</p> <p>ZoiteChat supports features such as: DCC, SASL, proxies, spellcheck, alerts, logging, custom themes, and Python/Perl scripts.</p>
</description> </description>
<url type="homepage">https://zoitechat.zoite.net</url> <url type="homepage">https://zoitechat.org</url>
<url type="bugtracker">https://github.com/zoitechat/zoitechat</url> <url type="bugtracker">https://github.com/zoitechat/zoitechat</url>
<url type="help">https://docs.zoitechat.zoite.net/en/latest/</url> <url type="help">https://docs.zoitechat.org/en/latest/</url>
<screenshots> <screenshots>
<screenshot type="default"> <screenshot type="default">
<image>https://zoitechat.zoite.net/assets/ZoiteChat.png</image> <image>https://zoitechat.org/assets/img/zoitechat-screenshot.png</image>
<caption>Main Chat Window</caption> <caption>Main Chat Window</caption>
</screenshot> </screenshot>
</screenshots> </screenshots>
@@ -29,6 +29,126 @@
<id>zoitechat.desktop</id> <id>zoitechat.desktop</id>
</provides> </provides>
<releases> <releases>
<release date="2026-09-15" version="2.19.1">
<description>
<ul>
<li>Fixed /msg messages showing twice on servers using IRCv3 <code>echo-message</code>.</li>
<li>Fixed escaping a leading command slash so <code>//text</code> sends <code>/text</code> as normal chat.</li>
<li>Fixed automatic away clearing when sending messages on servers using <code>echo-message</code>.</li>
<li>Fixed other users' away status not updating until rejoining a channel when <code>away-notify</code> updates are missed.</li>
<li>Fixed private messages and <code>/me</code> actions being routed to the wrong window when using WeeChat's IRC relay, while preserving channel ACTION routing.</li>
<li>Fixed a Flatpak startup error caused by the D-Bus service still using the old HexChat app ID.</li>
<li>Fixed optional Windows 10 theme downloads in the Windows installer.</li>
<li>Fixed the GTK warning when switching from channel tabs to the channel tree.</li>
<li>Fixed GTK3 theme archive root detection so themes with an <code>index.theme</code> file are preferred correctly.</li>
<li>Fixed source-build configuration when plugin AppStream metadata installation is disabled, and corrected the minimum Meson version to 0.59.</li>
</ul>
</description>
</release>
<release date="2026-07-28" version="2.19.0">
<description>
<ul>
<li>Added Python 3.8 compatibility for older Windows releases.</li>
<li>Added on-demand SASL password prompts, including renewed prompts after reconnecting.</li>
<li>Corrected NickServ authentication to use the decrypted password for the active network.</li>
<li>Made system theme matching the default and polished GTK3 theme integration.</li>
<li>Applied more settings changes immediately, reducing the need to restart ZoiteChat.</li>
<li>Added descriptive tooltips to the color picker choices and refined default text-event colors.</li>
<li>Stabilized channel tree font handling across palette and theme updates.</li>
<li>Corrected the chat scrollbar increment used by its arrow buttons.</li>
<li>Improved Windows URL launching through the native shell.</li>
<li>Restored expected minimize behavior when clicking ZoiteChat on the Windows taskbar.</li>
</ul>
</description>
</release>
<release date="2026-06-29" version="2.18.3">
<description>
<ul>
<li>Added IRCv3 support for message tags, echo-message, typing notifications, and replies.</li>
<li>Restored the accept-invalid-cert TLS bypass behavior.</li>
<li>Replaced legacy IPv4 APIs and added support for OpenSSL 4 APIs.</li>
<li>Snapshot Python hooks during plugin unload for safer plugin shutdown.</li>
<li>Fixed Windows GtkStatusIcon tray menu popup behavior.</li>
<li>Fixed self-echoed private messages routing to the target tab.</li>
<li>Fixed the lag ping timeout window.</li>
</ul>
</description>
</release>
<release date="2026-06-20" version="2.18.2">
<description>
<p>Security and connections:</p>
<ul>
<li>Improved TLS, certificate and hostname validation, FiSHLiM OpenSSL handling, keyring support, and DCC host lookups.</li>
</ul>
<p>FiSHLiM:</p>
<ul>
<li>Added a GTK key manager with context-menu access and clearer channel/private-message key handling.</li>
</ul>
<p>Interface and preferences:</p>
<ul>
<li>Improved keybind customization, reset support, channel-name switching, <code>/server -noproxy</code>, and stale-link ping checks.</li>
<li>Refined GTK behavior with native file choosers, topic layout preferences, timestamp/date tooltips, text scroll controls, and aligned network meters.</li>
<li>Added privacy and display options for hiding join/part hostmasks and controlling topic/multiline layout.</li>
</ul>
<p>Fixes:</p>
<ul>
<li>Fixed tray restore, DBus unload handling, GTK3 theme refresh/saving, topic relayout, and tab switcher compatibility.</li>
<li>Updated project documentation.</li>
</ul>
</description>
</release>
<release date="2026-05-21" version="2.18.1">
<description>
<ul>
<li>Migrated D-Bus handling to GDBus.</li>
<li>Enabled mouse wheel channel switching by default and consumed handled tab wheel events.</li>
<li>Switched main panes to native GTK scrollbars.</li>
<li>Tightened userlist button and meter layout.</li>
<li>Made the native Windows file chooser modal.</li>
<li>Set the Windows AppUserModelID before GTK startup.</li>
<li>Updated Windows CI to use the new GTK3 bundle zip.</li>
<li>Updated Windows installer architecture checks to use x64-compatible detection.</li>
<li>Bumped the Flatpak Perl runtime to 5.42.2.</li>
<li>Fixed palette color reads to preserve base GTK state.</li>
<li>Fixed auto-replace whole-word matching.</li>
<li>Fixed checksum file stream cleanup.</li>
<li>Fixed byte handling in the Python console.</li>
<li>Guarded GTK drag-and-drop handlers against null windows.</li>
<li>Fixed size_t and integer handling issues.</li>
<li>Removed the Winamp plugin.</li>
</ul>
</description>
</release>
<release date="2026-04-20" version="2.18.0">
<description>
<p>Tabs and navigation:</p>
<ul>
<li>Added optional close buttons on tabs.</li>
<li>Added Ctrl+W to close tabs and Ctrl+Shift+T to reopen recently closed tabs.</li>
<li>Improved channel tree behavior by ignoring collapsed parent selections.</li>
</ul>
<p>Themes and appearance:</p>
<ul>
<li>Expanded theme import support with .hct support in colors.conf import.</li>
<li>Added pevent import support from .hct theme files and improved import result messaging.</li>
<li>Expanded palette and selection CSS styling for better theme consistency.</li>
<li>Auto-sizes the topic bar to wrapped text for better multi-line topic handling.</li>
</ul>
<p>Links and text handling:</p>
<ul>
<li>Refined command-character parsing when pasting path-like text.</li>
<li>Tightened topic link detection and opening rules.</li>
<li>Added Public Suffix List validation for host links to reduce bad link matches.</li>
</ul>
<p>Packaging and platform integration:</p>
<ul>
<li>Sanitized the Linux open environment for AppImage builds and added safer fallback handling.</li>
<li>Labeled Windows installer runtimes with exact versions.</li>
<li>Fixed the notification icon and corrected the Flatpak screenshot asset.</li>
<li>Improved AppStream metainfo validation.</li>
</ul>
</description>
</release>
<release date="2026-03-30" version="2.18.0~pre6"> <release date="2026-03-30" version="2.18.0~pre6">
<description> <description>
<p>GTK theme and UI:</p> <p>GTK theme and UI:</p>
@@ -100,14 +220,6 @@
</ul> </ul>
</description> </description>
</release> </release>
<release date="2026-03-22" version="2.18.0~pre5">
<description>
<p>Version metadata update:</p>
<ul>
<li>Bumped release version references to <code>2.18.0~pre5</code> across build and packaging files.</li>
</ul>
</description>
</release>
<release date="2026-03-14" version="2.18.0~pre4"> <release date="2026-03-14" version="2.18.0~pre4">
<description> <description>
<p>UI fixes, topic bar improvements, and selection styling updates:</p> <p>UI fixes, topic bar improvements, and selection styling updates:</p>
+15
View File
@@ -0,0 +1,15 @@
# Source https://coderwall.com/p/wws2uq
sudo: false
language: python
python:
- "2.7"
cache:
pip: true
custom_install: true
# command to install dependencies
install: pip install --upgrade Sphinx
# command to run tests
script: sphinx-build -n -b html -d _build/doctrees . _build/html
# Flags used here, not in `make html`:
# -n Run in nit-picky mode. Currently, this generates warnings for all missing references.
# -W Turn warnings into errors. This means that the build stops at the first warning and sphinx-build exits with exit status 1.
+94
View File
@@ -0,0 +1,94 @@
[main]
host = https://www.transifex.com
type = PO
[zoitechat-documentation.addons]
file_filter = po/<lang>/LC_MESSAGES/addons.po
source_file = _build/locale/addons.pot
source_lang = en
[zoitechat-documentation.appearance]
file_filter = po/<lang>/LC_MESSAGES/appearance.po
source_file = _build/locale/appearance.pot
source_lang = en
[zoitechat-documentation.building]
file_filter = po/<lang>/LC_MESSAGES/building.po
source_file = _build/locale/building.pot
source_lang = en
[zoitechat-documentation.changelog]
file_filter = po/<lang>/LC_MESSAGES/changelog.po
source_file = _build/locale/changelog.pot
source_lang = en
[zoitechat-documentation.commands]
file_filter = po/<lang>/LC_MESSAGES/commands.po
source_file = _build/locale/commands.pot
source_lang = en
#[zoitechat-documentation.developers]
#file_filter = po/<lang>/LC_MESSAGES/developers.po
#source_file = _build/locale/developers.pot
#source_lang = en
[zoitechat-documentation.faq]
file_filter = po/<lang>/LC_MESSAGES/faq.po
source_file = _build/locale/faq.pot
source_lang = en
#[zoitechat-documentation.getting_started]
#file_filter = po/<lang>/LC_MESSAGES/getting_started.po
#source_file = _build/locale/getting_started.pot
#source_lang = en
#[zoitechat-documentation.helping]
#file_filter = po/<lang>/LC_MESSAGES/helping.po
#source_file = _build/locale/helping.pot
#source_lang = en
#[zoitechat-documentation.index]
#file_filter = po/<lang>/LC_MESSAGES/index.po
#source_file = _build/locale/index.pot
#source_lang = en
#[zoitechat-documentation.perl_modules]
#file_filter = po/<lang>/LC_MESSAGES/perl_modules.po
#source_file = _build/locale/perl_modules.pot
#source_lang = en
#[zoitechat-documentation.plugins]
#file_filter = po/<lang>/LC_MESSAGES/plugins.po
#source_file = _build/locale/plugins.pot
#source_lang = en
#[zoitechat-documentation.script_dbus]
#file_filter = po/<lang>/LC_MESSAGES/script_dbus.po
#source_file = _build/locale/script_dbus.pot
#source_lang = en
#[zoitechat-documentation.script_javascript]
#file_filter = po/<lang>/LC_MESSAGES/script_javascript.po
#source_file = _build/locale/script_javascript.pot
#source_lang = en
#[zoitechat-documentation.script_perl]
#file_filter = po/<lang>/LC_MESSAGES/script_perl.po
#source_file = _build/locale/script_perl.pot
#source_lang = en
#[zoitechat-documentation.script_python]
#file_filter = po/<lang>/LC_MESSAGES/script_python.po
#source_file = _build/locale/script_python.pot
#source_lang = en
[zoitechat-documentation.settings]
file_filter = po/<lang>/LC_MESSAGES/settings.po
source_file = _build/locale/settings.pot
source_lang = en
[zoitechat-documentation.tips]
file_filter = po/<lang>/LC_MESSAGES/tips.po
source_file = _build/locale/tips.pot
source_lang = en
+157
View File
@@ -0,0 +1,157 @@
# Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = _build
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
# the i18n builder cannot share the environment and doctrees with the others
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " html to make standalone HTML files"
@echo " dirhtml to make HTML files named index.html in directories"
@echo " singlehtml to make a single large HTML file"
@echo " pickle to make pickle files"
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " qthelp to make HTML files and a qthelp project"
@echo " devhelp to make HTML files and a Devhelp project"
@echo " epub to make an epub"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " latexpdf to make LaTeX files and run them through pdflatex"
@echo " text to make text files"
@echo " man to make manual pages"
@echo " texinfo to make Texinfo files"
@echo " info to make Texinfo files and run them through makeinfo"
@echo " gettext to make PO message catalogs"
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
clean:
-rm -rf $(BUILDDIR)/*
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
dirhtml:
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
singlehtml:
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
@echo
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
pickle:
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
@echo
@echo "Build finished; now you can process the pickle files."
json:
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
@echo
@echo "Build finished; now you can process the JSON files."
htmlhelp:
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in $(BUILDDIR)/htmlhelp."
qthelp:
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/ZoiteChat.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/ZoiteChat.qhc"
devhelp:
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
@echo
@echo "Build finished."
@echo "To view the help file:"
@echo "# mkdir -p $$HOME/.local/share/devhelp/ZoiteChat"
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/ZoiteChat"
@echo "# devhelp"
epub:
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
@echo
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
latex:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
@echo "Run \`make' in that directory to run these through (pdf)latex" \
"(use \`make latexpdf' here to do that automatically)."
latexpdf:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through pdflatex..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
text:
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
@echo
@echo "Build finished. The text files are in $(BUILDDIR)/text."
man:
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
@echo
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
texinfo:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
@echo "Run \`make' in that directory to run these through makeinfo" \
"(use \`make info' here to do that automatically)."
info:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo "Running Texinfo files through makeinfo..."
make -C $(BUILDDIR)/texinfo info
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
gettext:
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
@echo
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
@echo "The overview file is in $(BUILDDIR)/changes."
linkcheck:
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in $(BUILDDIR)/linkcheck/output.txt."
doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."
server:
@cd $(BUILDDIR)/html
@python -u -m SimpleHTTPServer
+90
View File
@@ -0,0 +1,90 @@
# ZoiteChat documentation
## Getting started
* Install [Python 3](https://www.python.org/)
* Install pip for Python 3
* Install Sphinx: `python -m pip install sphinx`
* On Windows: ensure your Python 3 installation is on `PATH`
* Run `make html` to build the HTML files
* Run the Python webserver to test your changes: `make server`
(http://localhost:8000/)
## Conventions
* 4 Spaces are used for indentation.
* Ordered or unordered lists use visual indentation, where any non-list
subelement would use an indentation of a multiple of 4 again.
Example:
```rest
- a list item
- a list item
with a visually indented continuation
- a list item with a sub-list
- that is also visually indented
- a list item with a code block
.. code-block:: none
that is not visually indented
```
## HTML theme
The `zoitechat` theme inherits Sphinx's built-in `basic` theme. It uses the
same compact layout, blue navigation, and system fonts as zoitechat.org.
Appearance starts in System mode on each page. Light and Dark are available
for the current page; no cookies or browser storage are used.
The theme's templates are in `_theme/zoitechat/`. Its readable CSS and
JavaScript are in `_static/css/zoitechat-docs.css` and
`_static/js/zoitechat-docs.js`. No minification, JavaScript package manager,
external fonts, or CDN assets are required.
Navigation URLs, including `zoite_atlas_url`, are configured through
`html_context` in `conf.py`. The logo
is `_static/zoitechat-logo.svg`. Standard Sphinx search, indexes, source
links, anchors, translations, and previous/next links remain available.
The HTML output also works as locally installed documentation; project-site
links only access the network when clicked.
Build from the repository root:
```sh
sphinx-build -b html docs docs/_build/html
```
Use a clean output directory when switching themes so stale assets from
earlier builds are not included in a package.
## Debian packaging
The `zoitechat` theme uses Sphinx's built-in `basic` theme. It does not
bundle Bootstrap, jQuery, jQuery Cookie, Font Awesome, or icon fonts.
Its font stack includes DejaVu Sans and DejaVu Sans Mono; no font files
are bundled by the theme.
No pre-minified third-party theme assets need to be added or rebuilt.
The Meson option `-Dinstall-docs` defaults to `false`, so platforms that
do not ship offline documentation do not need Sphinx just to build ZoiteChat.
Pass `-Dinstall-docs=true` when packaging local HTML documentation. Fedora
and AppImage enable it explicitly; the Debian documentation package can do
the same.
Build with the distribution's `python3-sphinx`. After installing the HTML
into the package staging directory, use `dh_sphinxdoc` (or `dh --with
sphinxdoc`) to replace Sphinx's standard JavaScript files with system
symlinks. Include `${sphinxdoc:Depends}` in the package dependencies and
`${sphinxdoc:Built-Using}` where required for generated assets. The custom
`zoitechat-docs.js` remains a small, readable project source file.
A built-in theme is still selectable with `-D html_theme=classic` if a
packager needs a different layout. The old RST content has existing build
diagnostics; changing the theme does not suppress or alter them.
Shared website chrome is in `_static/css/zoitechat-site.css`, copied from the main site. Keep that readable stylesheet in sync when changing the shared header, navigation, typography, or colours.
Binary file not shown.
+843
View File
@@ -0,0 +1,843 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/* Keep the docs close to the desktop client and the main project's website. */
:root {
color-scheme: light;
--page: #ffffff;
--panel: #f1f2f3;
--text: #25282c;
--muted: #58616b;
--line: #bdc3c9;
--soft-line: #dce0e4;
--blue: #215a9e;
--link: #174f92;
--selection: #e6effa;
--code: #f2f4f6;
--comment: #58616b;
--string: #236741;
--number: #7842ab;
--font: system-ui, -apple-system, "Segoe UI", "DejaVu Sans", sans-serif;
--mono: Consolas, "DejaVu Sans Mono", "Liberation Mono", monospace;
}
html[data-theme="dark"] {
color-scheme: dark;
--page: #191c1f;
--panel: #25292d;
--text: #eef0f2;
--muted: #b2bbc4;
--line: #50575e;
--soft-line: #353c43;
--blue: #2865a8;
--link: #8dc0ff;
--selection: #233951;
--code: #111518;
--comment: #b2bbc4;
--string: #92d5ab;
--number: #dfacff;
}
@media (prefers-color-scheme: dark) {
:root:not([data-theme]) {
color-scheme: dark;
--page: #191c1f;
--panel: #25292d;
--text: #eef0f2;
--muted: #b2bbc4;
--line: #50575e;
--soft-line: #353c43;
--blue: #2865a8;
--link: #8dc0ff;
--selection: #233951;
--code: #111518;
--comment: #b2bbc4;
--string: #92d5ab;
--number: #dfacff;
}
}
* {
box-sizing: border-box;
}
html {
scroll-padding-top: 16px;
}
body {
margin: 0;
padding: 0;
background: var(--page);
color: var(--text);
font: 14px/1.7 var(--font);
}
a,
a:visited {
color: var(--link);
text-underline-offset: 3px;
}
a:hover {
text-decoration: underline;
}
:focus-visible {
outline: 3px solid var(--link);
outline-offset: 3px;
}
[hidden] {
display: none !important;
}
button,
input,
select {
font: inherit;
}
button,
summary,
select {
touch-action: manipulation;
}
button,
summary {
cursor: pointer;
}
img {
max-width: 100%;
height: auto;
}
h1,
h2,
h3,
h4 {
color: var(--text);
font-family: var(--font);
font-weight: 600;
line-height: 1.35;
}
h1 {
margin: 0 0 20px;
font-size: 28px;
letter-spacing: -0.5px;
}
h2 {
margin: 28px 0 12px;
font-size: 21px;
}
h3 {
margin: 22px 0 10px;
font-size: 17px;
}
h4 {
font-size: 15px;
}
p {
margin: 0 0 15px;
}
input,
select,
button {
min-height: 34px;
border: 1px solid var(--line);
border-radius: 2px;
padding: 6px 9px;
background: var(--page);
color: var(--text);
}
input[type="submit"],
button {
background: var(--panel);
}
input[type="submit"]:hover,
button:hover {
background: var(--selection);
}
.skip-link {
position: absolute;
top: -100px;
left: 15px;
z-index: 10;
padding: 10px;
background: var(--page);
}
.skip-link:focus {
top: 10px;
}
.site-header,
.docs-layout,
.site-footer {
width: min(1200px, calc(100% - 48px));
margin-inline: auto;
}
.site-header {
padding-top: 32px;
}
.topbar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 20px;
padding-bottom: 16px;
}
.brand {
display: flex;
align-items: center;
gap: 12px;
color: var(--text) !important;
text-decoration: none;
}
.brand img {
display: block;
width: 38px;
height: 42px;
}
.brand strong {
font-size: 28px;
font-weight: 650;
letter-spacing: -0.8px;
}
.appearance {
display: flex;
align-items: center;
gap: 10px;
color: var(--muted);
font-size: 11px;
}
.appearance select {
min-height: 30px;
padding: 3px 7px;
font-size: 12px;
}
.site-nav {
display: flex;
flex-wrap: wrap;
align-items: stretch;
border: 1px solid var(--line);
background: var(--panel);
}
.site-nav > a,
.support-menu > summary {
display: flex;
align-items: center;
min-height: 36px;
padding: 6px 15px;
color: var(--text);
font-size: 13px;
text-decoration: none;
}
.site-nav > a:hover,
.support-menu > summary:hover {
background: var(--selection);
color: var(--link);
}
.site-nav > a.selected {
background: var(--blue);
color: #ffffff;
}
.support-menu {
position: relative;
}
.support-menu > summary {
list-style: none;
}
.support-menu > summary::after {
content: "⌄";
margin-left: 8px;
}
.support-menu > summary::-webkit-details-marker {
display: none;
}
.support-links {
position: absolute;
right: 0;
top: 100%;
z-index: 5;
min-width: 235px;
padding: 5px;
border: 1px solid var(--line);
background: var(--page);
}
.support-links a {
display: block;
padding: 10px 12px;
text-decoration: none;
}
.support-links a:hover {
background: var(--selection);
}
.support-links small {
display: block;
color: var(--muted);
font-size: 11px;
}
.docs-location {
display: flex;
justify-content: space-between;
align-items: start;
gap: 20px;
margin-top: 12px;
color: var(--muted);
font-size: 11px;
}
.docs-location nav {
min-width: 0;
overflow-wrap: anywhere;
}
.docs-location nav > span {
margin-inline: 5px;
}
.docs-version {
flex-shrink: 0;
font-family: var(--mono);
}
.docs-layout {
display: grid;
grid-template-columns: 240px minmax(0, 1fr);
align-items: start;
gap: 36px;
padding-block: 26px 30px;
}
.docs-layout.no-sidebar {
display: block;
}
.docs-sidebar {
position: sticky;
top: 16px;
max-height: calc(100vh - 32px);
overflow: auto;
padding-right: 15px;
border-right: 1px solid var(--soft-line);
font-size: 13px;
}
.docs-navigation > summary {
margin-bottom: 14px;
padding: 5px 0;
color: var(--text);
font-weight: 600;
}
.docs-sidebar h2,
.docs-sidebar h3 {
margin: 20px 0 8px;
padding-bottom: 5px;
border-bottom: 1px solid var(--line);
font-size: 14px;
letter-spacing: 0;
}
.docs-sidebar ul {
margin: 0;
padding-left: 17px;
list-style: none;
}
.docs-sidebar nav > ul {
padding-left: 0;
}
.docs-sidebar li {
margin: 5px 0;
line-height: 1.55;
}
.docs-sidebar li.current > a,
.docs-sidebar a.current {
font-weight: 600;
}
.docs-sidebar .current > a {
text-decoration-thickness: 2px;
}
.docs-search > label {
display: block;
margin-bottom: 5px;
color: var(--muted);
font-size: 11px;
}
.search-inputs {
display: flex;
align-items: stretch;
gap: 5px;
}
.search-inputs input {
width: 100%;
min-width: 0;
font-size: 12px;
}
.search-inputs button {
flex-shrink: 0;
padding-inline: 7px;
font-size: 11px;
}
.docs-content {
min-width: 0;
overflow-wrap: break-word;
}
.docs-content > :first-child,
.docs-content > section:first-child > :first-child {
margin-top: 0;
}
.docs-content li {
margin-bottom: 5px;
}
.docs-content dl {
margin-bottom: 20px;
}
.docs-content dt {
margin-top: 14px;
color: var(--text);
}
.docs-content dd {
margin-left: 24px;
}
.docs-content code,
.docs-content pre,
.docs-content kbd {
font-family: var(--mono);
font-size: 0.9em;
}
.docs-content code.literal {
padding: 1px 4px;
border: 1px solid var(--soft-line);
background: var(--code);
}
.docs-content pre {
margin: 0;
white-space: pre;
line-height: 1.7;
}
.docs-content .highlight,
.docs-content .literal-block {
max-width: 100%;
margin: 16px 0;
padding: 14px;
overflow-x: auto;
border: 1px solid var(--line);
background: var(--code);
color: var(--text);
}
.highlight pre {
background: transparent;
}
.highlight span {
color: var(--text) !important;
}
.highlight .k,
.highlight .kc,
.highlight .kd,
.highlight .kn,
.highlight .kp,
.highlight .kr,
.highlight .kt,
.highlight .o,
.highlight .ow,
.highlight .nb,
.highlight .nf {
color: var(--link) !important;
}
.highlight .c,
.highlight .c1,
.highlight .cm,
.highlight .cp,
.highlight .cs,
.highlight .sd {
color: var(--comment) !important;
}
.highlight .s,
.highlight .s1,
.highlight .s2,
.highlight .sa,
.highlight .sb,
.highlight .sc,
.highlight .se,
.highlight .sh,
.highlight .si,
.highlight .sr,
.highlight .ss {
color: var(--string) !important;
}
.highlight .m,
.highlight .mb,
.highlight .mf,
.highlight .mh,
.highlight .mi,
.highlight .mo,
.highlight .il {
color: var(--number) !important;
}
table.docutils,
table.indextable,
table.genindextable {
width: 100%;
margin: 16px 0;
border-collapse: collapse;
font-size: 13px;
}
table.docutils th,
table.docutils td {
padding: 9px 12px;
border: 1px solid var(--line);
text-align: left;
vertical-align: top;
}
table.docutils th {
background: var(--panel);
}
table.docutils tbody tr:nth-child(even) {
background: var(--panel);
}
table.genindextable td,
table.indextable td {
vertical-align: top;
}
.docs-content .admonition,
.docs-content .topic,
.docs-content .sidebar {
float: none;
width: auto;
margin: 18px 0;
padding: 12px 16px;
border: 1px solid var(--soft-line);
border-left: 3px solid var(--blue);
background: var(--panel);
color: var(--text);
}
.docs-content .admonition-title,
.docs-content .topic-title,
.docs-content .sidebar-title {
margin-bottom: 6px;
font-size: 14px;
font-weight: 600;
}
.docs-content .admonition > :last-child,
.docs-content .topic > :last-child {
margin-bottom: 0;
}
.docs-content img,
.docs-content .figure {
max-width: 100%;
}
.docs-content img {
height: auto;
}
.docs-content .figure {
margin-inline: 0;
}
.docs-content .caption {
color: var(--muted);
font-size: 12px;
}
a.headerlink {
padding-left: 6px;
color: var(--muted);
font-size: 0.8em;
text-decoration: none;
visibility: hidden;
}
h1:hover > .headerlink,
h2:hover > .headerlink,
h3:hover > .headerlink,
h4:hover > .headerlink,
dt:hover > .headerlink,
a.headerlink:focus-visible {
visibility: visible;
}
.highlighted {
background: var(--selection);
color: var(--text);
}
.page-relations {
display: flex;
justify-content: space-between;
gap: 24px;
margin-top: 32px;
padding-top: 16px;
border-top: 1px solid var(--line);
font-size: 13px;
}
.page-relations > div {
max-width: 46%;
}
.page-relations > div:last-child {
margin-left: auto;
text-align: right;
}
.page-relations small {
display: block;
color: var(--muted);
font-size: 11px;
}
.site-footer {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
gap: 12px 20px;
padding: 17px 0 28px;
border-top: 1px solid var(--line);
color: var(--muted);
font-size: 11px;
}
.site-footer nav {
display: flex;
flex-wrap: wrap;
gap: 5px 16px;
}
@media (max-width: 1000px) {
.docs-layout {
grid-template-columns: 220px minmax(0, 1fr);
gap: 26px;
}
.site-nav > a,
.support-menu > summary {
padding-inline: 10px;
}
}
@media (max-width: 760px) {
.site-header,
.docs-layout,
.site-footer {
width: calc(100% - 30px);
}
.site-header {
padding-top: 18px;
}
.brand strong {
font-size: 25px;
}
.brand img {
width: 32px;
height: 36px;
}
.appearance > span {
display: none;
}
.site-nav > a,
.support-menu > summary {
min-height: 38px;
padding: 7px 9px;
font-size: 12px;
}
.docs-layout {
display: block;
padding-top: 18px;
}
.docs-sidebar {
position: static;
max-height: none;
margin-bottom: 23px;
padding: 0;
border: 0;
}
.docs-navigation {
padding: 7px 12px;
border: 1px solid var(--line);
background: var(--panel);
}
.docs-navigation > summary {
margin: 0;
}
.sidebar-content {
padding-top: 12px;
}
.docs-sidebar ul {
margin-bottom: 12px;
}
.docs-sidebar .sidebar-content {
max-height: 60vh;
overflow: auto;
}
h1 {
font-size: 26px;
}
.docs-version {
font-size: 10px;
}
.support-links {
right: 0;
min-width: 210px;
}
.docs-content table {
display: block;
max-width: 100%;
overflow-x: auto;
}
}
@media print {
.site-header,
.docs-sidebar,
.site-footer,
.page-relations,
a.headerlink {
display: none;
}
.docs-layout,
.docs-layout.no-sidebar {
display: block;
width: 100%;
padding: 0;
}
.docs-content,
.docs-content * {
color: #000000 !important;
background: #ffffff !important;
}
.docs-content .highlight,
.docs-content .literal-block {
overflow: visible;
}
.docs-content pre {
white-space: pre-wrap;
}
.docs-content .highlight span {
color: #000000 !important;
}
}
/* The shared site stylesheet owns the page shell and primary navigation. */
:root {
--page: var(--bg);
--panel: var(--chrome);
--font: system-ui, -apple-system, "Segoe UI", "DejaVu Sans", sans-serif;
}
.site-header { padding: 0; }
.appearance select { min-height: 0; border-radius: 3px; }
.docs-layout {
width: min(1040px, calc(100% - 48px));
grid-template-columns: minmax(0, 1fr) 220px;
gap: 40px;
padding-block: 28px 20px;
}
.docs-content { grid-column: 1; grid-row: 1; }
.docs-sidebar {
grid-column: 2;
grid-row: 1;
padding: 3px 0 0;
border: 0;
}
.site-footer { display: block; }
.site-footer .footer-links { gap: 5px 14px; }
.docs-location { width: 100%; margin-top: 0; margin-bottom: 20px; }
@media (max-width: 800px) {
.docs-layout { grid-template-columns: minmax(0, 1fr) 185px; gap: 26px; }
}
@media (max-width: 760px) {
.docs-layout { display: block; }
}
@media (max-width: 600px) {
.docs-layout { width: calc(100% - 30px); padding-top: 23px; }
}
@media print {
.docs-layout, .docs-layout.no-sidebar { width: 100%; padding: 0; }
}
.site-footer, .footer-grid { font-size: inherit; color: inherit; }
+526
View File
@@ -0,0 +1,526 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/* Shared chrome copied from ZoiteChat/site assets/css/site.css (1df996a). */
:root {
color-scheme: light;
--bg: #fff;
--surface: #fff;
--chrome: #f1f2f3;
--chrome2: #e3e5e7;
--text: #25282c;
--muted: #58616b;
--line: #bdc3c9;
--soft-line: #dce0e4;
--blue: #215a9e;
--link: #174f92;
--selection: #e6effa;
--success: #236741;
--code: #f2f4f6;
--shadow: none;
--mono: Consolas,"DejaVu Sans Mono","Liberation Mono",monospace;
}
html[data-theme=dark] {
color-scheme: dark;
--bg: #111416;
--surface: #191c1f;
--chrome: #25292d;
--chrome2: #30353a;
--text: #eef0f2;
--muted: #b2bbc4;
--line: #50575e;
--soft-line: #353c43;
--blue: #2865a8;
--link: #8dc0ff;
--selection: #233951;
--success: #92d5ab;
--code: #111518;
--shadow: none;
}
* {
box-sizing: border-box;
}
body {
margin: 0;
background: var(--bg);
color: var(--text);
font: 15px/1.6 system-ui,-apple-system,"Segoe UI","DejaVu Sans",sans-serif;
}
a {
color: var(--link);
text-underline-offset: 3px;
}
a:hover {
text-decoration: underline;
}
button, input, select {
font: inherit;
}
button, summary {
cursor: pointer;
}
img {
display: block;
max-width: 100%;
height: auto;
}
button, a, input, select, summary {
touch-action: manipulation;
}
:focus-visible {
outline: 3px solid var(--link);
outline-offset: 3px;
}
[hidden] {
display: none!important;
}
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip-path: inset(50%);
white-space: nowrap;
border: 0;
}
.skip-link {
position: absolute;
left: 12px;
top: -100px;
z-index: 10;
padding: 10px;
background: var(--surface);
}
.skip-link:focus {
top: 12px;
}
.topbar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 20px;
padding: 0 0 16px;
background: var(--surface);
border-bottom: 1px solid var(--line);
border: 0;
}
.brand {
display: flex;
gap: 12px;
align-items: center;
color: var(--text);
text-decoration: none;
}
.brand img {
width: 38px;
height: 42px;
}
.brand strong {
font-size: 28px;
letter-spacing: -.8px;
font-weight: 650;
}
.brand small {
display: block;
font-size: 12px;
color: var(--muted);
line-height: 1.2;
}
.appearance {
display: flex;
align-items: center;
gap: 10px;
font-size: 11px;
color: var(--muted);
}
select {
border: 1px solid var(--line);
border-radius: 3px;
padding: 5px 8px;
background: var(--surface);
color: var(--text);
}
.navigation {
display: flex;
align-items: center;
justify-content: flex-start;
gap: 12px;
background: var(--chrome);
border-bottom: 1px solid var(--line);
padding: 0;
border: 1px solid var(--line);
border-radius: 0;
}
.nav-links {
display: flex;
align-items: stretch;
flex-wrap: wrap;
gap: 0;
}
.nav-links>a, .nav-menu summary {
display: flex;
align-items: center;
min-height: 36px;
padding: 6px 15px;
color: var(--text);
font-size: 13px;
text-decoration: none;
border-bottom: 0;
}
.nav-links>a[aria-current=page] {
color: #fff;
border-bottom-color: var(--blue);
font-weight: 500;
background: var(--blue);
}
.nav-links>a:hover, .nav-menu summary:hover {
background: var(--selection);
}
.nav-menu {
position: relative;
}
.nav-menu summary {
list-style: none;
}
.nav-menu summary::after {
content: "⌄";
margin-left: 9px;
}
.nav-menu summary::-webkit-details-marker {
display: none;
}
.nav-menu-panel {
position: absolute;
right: 0;
top: 100%;
z-index: 5;
min-width: 280px;
background: var(--surface);
border: 1px solid var(--line);
box-shadow: var(--shadow);
padding: 5px;
}
.nav-menu-panel a {
display: block;
padding: 10px 12px;
text-decoration: none;
}
.nav-menu-panel a:hover {
background: var(--selection);
}
.nav-menu-panel span {
display: block;
font-size: 12px;
color: var(--muted);
}
.site-main {
min-height: 0;
}
h1, h2, h3, p {
margin-top: 0;
}
h1 {
font-size: clamp(30px,3.9vw,46px);
line-height: 1.16;
letter-spacing: -1.5px;
font-weight: 650;
margin-bottom: 20px;
}
h2 {
font-size: 20px;
line-height: 1.3;
letter-spacing: 0;
margin-bottom: 12px;
}
h3 {
font-size: 16px;
line-height: 1.4;
}
p {
color: var(--text);
font-size: 14px;
line-height: 1.7;
margin-bottom: 15px;
}
.site-footer {
padding: 17px 0;
background: var(--surface);
border-top: 1px solid var(--line);
}
.footer-grid {
display: flex;
align-items: start;
justify-content: space-between;
gap: 20px;
}
.footer-copy {
font-size: 11px;
max-width: 62ch;
margin: 0;
color: var(--muted);
}
.footer-links {
display: flex;
flex-wrap: wrap;
gap: 5px 14px;
max-width: 400px;
font-size: 11px;
}
@media (max-width:1000px) {
.navigation {
padding: 0 15px;
}
.nav-links>a, .nav-menu summary {
padding: 8px 10px;
}
}
@media (max-width:650px) {
.topbar {
padding: 13px 16px;
gap: 8px;
}
.brand strong {
font-size: 20px;
}
.brand small {
font-size: 10px;
}
.brand img {
width: 28px;
height: 31px;
}
.appearance {
gap: 5px;
}
.appearance>span {
display: none;
}
.appearance select {
font-size: 12px;
max-width: 98px;
}
.navigation {
padding: 0 6px;
}
.nav-links {
gap: 0;
}
.nav-links>a, .nav-menu summary {
min-height: 44px;
padding: 8px;
font-size: 12px;
}
h1 {
font-size: 34px;
letter-spacing: -1px;
}
.footer-grid {
display: grid;
gap: 20px;
}
.site-footer {
padding: 20px 17px;
}
.nav-menu-panel {
position: fixed;
right: 10px;
left: 10px;
top: auto;
min-width: 0;
}
}
@media (prefers-reduced-motion:reduce) {
* {
scroll-behavior: auto!important;
}
}
.appearance select {
font-size: 12px;
padding: 3px 7px;
}
.nav-links>a:hover {
background: var(--selection);
color: var(--link);
}
h1 {
font-size: 28px;
font-weight: 600;
letter-spacing: -.5px;
line-height: 1.3;
margin: 0 0 16px;
max-width: none;
}
@media (max-width:1100px) {
.navigation {
padding: 0;
}
}
@media (max-width:800px) {
.nav-links>a, .nav-menu summary {
padding: 6px 10px;
}
}
@media (max-width:600px) {
.topbar {
padding: 0 0 14px;
}
.brand strong {
font-size: 25px;
}
.brand img {
width: 32px;
height: 36px;
}
.nav-links>a, .nav-menu summary {
padding: 7px 9px;
min-height: 38px;
font-size: 12px;
}
.footer-grid {
display: block;
}
.footer-links {
margin-top: 10px;
max-width: none;
}
.site-footer {
padding: 14px 0;
}
h1 {
font-size: 26px;
}
}
@media (prefers-color-scheme: dark) {
:root:not([data-theme]) {
color-scheme: dark;
--bg: #111416;
--surface: #191c1f;
--chrome: #25292d;
--chrome2: #30353a;
--text: #eef0f2;
--muted: #b2bbc4;
--line: #50575e;
--soft-line: #353c43;
--blue: #2865a8;
--link: #8dc0ff;
--selection: #233951;
--success: #92d5ab;
--code: #111518;
--shadow: none;
}
}
.site-header, .site-main, .site-footer {
width: min(1040px, calc(100% - 48px));
margin-inline: auto;
}
.site-header {
margin-top: 32px;
}
.site-main {
padding-block: 28px 20px;
}
.site-header .topbar {
padding: 0 0 16px;
}
.site-header .navigation {
padding: 0;
}
@media (max-width: 600px) {
.site-header, .site-main, .site-footer {
width: calc(100% - 30px);
}
.site-header {
margin-top: 18px;
}
.site-main {
padding-top: 23px;
}
}
/* Match the browser's heading defaults used by the main site. */
h2, h3 { font-weight: bold; }
h2 { margin-top: .83em; }
h3 { margin-block: 1em; }
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

+44
View File
@@ -0,0 +1,44 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
(() => {
'use strict';
const initialise = () => {
const picker = document.getElementById('theme-picker');
const preference = window.matchMedia('(prefers-color-scheme: dark)');
if (picker instanceof HTMLSelectElement) {
const applyTheme = () => {
const dark = picker.value === 'dark'
|| (picker.value === 'system' && preference.matches);
document.documentElement.dataset.theme = dark ? 'dark' : 'light';
document.querySelectorAll('meta[name="theme-color"]').forEach((meta) => {
meta.setAttribute('content', dark ? '#111416' : '#ffffff');
});
};
// Follow the OS on every visit, including browser back/forward navigation.
const resetTheme = () => {
picker.value = 'system';
applyTheme();
};
picker.addEventListener('change', applyTheme);
preference.addEventListener('change', applyTheme);
window.addEventListener('pageshow', resetTheme);
resetTheme();
}
const navigation = document.getElementById('docs-navigation');
const compact = window.matchMedia('(max-width: 760px)');
if (navigation instanceof HTMLDetailsElement) {
const fitNavigation = () => { navigation.open = !compact.matches; };
compact.addEventListener('change', fitNavigation);
fitNavigation();
}
};
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initialise, { once: true });
} else {
initialise();
}
})();
+56
View File
@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 908.34 1000">
<defs>
<style>
.cls-1 {
fill: url(#linear-gradient-2);
}
.cls-2 {
fill: #fff;
}
.cls-3 {
isolation: isolate;
}
.cls-4 {
opacity: .3;
}
.cls-5 {
mix-blend-mode: multiply;
opacity: .5;
}
.cls-6 {
fill: url(#linear-gradient);
}
</style>
<linearGradient id="linear-gradient" x1="14.94" y1="500" x2="893.4" y2="500" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#01a1fb"/>
<stop offset="1" stop-color="#0047bc"/>
</linearGradient>
<linearGradient id="linear-gradient-2" x1="14.94" y1="533.09" x2="823" y2="533.09" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#0047bc"/>
<stop offset="1" stop-color="#01a1fb"/>
</linearGradient>
</defs>
<g class="cls-3">
<g id="Layer_1" data-name="Layer 1">
<g>
<g>
<path class="cls-6" d="m209.4,878.26c7.44-16.01,13.21-32.36,17.4-48.87C99.81,752.4,14.94,612.88,14.94,453.52c0-242.58,196.65-439.23,439.23-439.23,242.58,0,439.23,196.65,439.23,439.23,0,242.58-196.65,439.23-439.23,439.23-33.13,0-65.4-3.69-96.43-10.65-57.19,71.24-145.98,109.28-235.84,102.91,36.55-26.73,67.06-62.8,87.5-106.76Z"/>
<path class="cls-1" d="m209.4,878.26c7.41-15.92,13.14-32.19,17.33-48.6C99.79,752.82,14.94,613.44,14.94,454.23c0-158.08,83.65-296.6,209.09-373.76-43.67,68.18-68.99,149.24-68.99,236.21,0,242.19,196.33,438.52,438.52,438.52,84.11,0,162.68-23.7,229.43-64.76-77.94,121.67-214.32,202.31-369.53,202.31-32.92,0-64.98-3.65-95.82-10.53-57.19,71.17-145.94,109.16-235.75,102.79,36.55-26.73,67.06-62.8,87.5-106.76Z"/>
<g class="cls-4">
<path class="cls-2" d="m815.15,496.56c0-214.21-173.66-387.87-387.87-387.87-125.21,0-236.54,59.35-307.46,151.43,66.98-116.27,192.51-194.55,336.33-194.55,214.22,0,387.87,173.66,387.87,387.87,0,89-30,170.99-80.41,236.44,32.78-56.91,51.54-122.92,51.54-193.32Z"/>
</g>
</g>
<g>
<image class="cls-5" width="316" height="274" transform="translate(209.32 236.76) scale(1.59)" xlink:href="data:image/png;base64,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 truncated
<path class="cls-2" d="m683.68,250.22h-421.95l-38.92,93.77h249.4l-249.4,300.48h422.01l38.86-93.84h-249.33l249.33-300.42Z"/>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 12 KiB

+5
View File
@@ -0,0 +1,5 @@
{# SPDX-License-Identifier: GPL-2.0-or-later #}
<nav class="global-toc" aria-label="{{ _('Documentation contents') }}">
<h2>{{ _('Contents') }}</h2>
{{ toctree(maxdepth=2, collapse=true, includehidden=true, titles_only=true) }}
</nav>
+108
View File
@@ -0,0 +1,108 @@
{# SPDX-License-Identifier: GPL-2.0-or-later #}
{% extends "basic/layout.html" %}
{% set docs_root = root_doc|default(master_doc) %}
{% block extrahead %}
{{ super() }}
<meta name="theme-color" content="#ffffff" media="(prefers-color-scheme: light)">
<meta name="theme-color" content="#111416" media="(prefers-color-scheme: dark)">
{% endblock %}
{% block body_tag %}<body id="top">{% endblock %}
{% block header %}
<a class="skip-link" href="#main-content">{{ _('Skip to content') }}</a>
<header class="site-header">
<div class="topbar">
<a class="brand" href="{{ pathto(docs_root)|e }}">
<img src="{{ logo_url|default(pathto('_static/zoitechat-logo.svg', 1), true)|e }}" alt="" width="38" height="42">
<strong>{{ project|e }}</strong>
</a>
<label class="appearance" for="theme-picker">
<span>{{ _('Appearance') }}</span>
<select id="theme-picker" aria-label="{{ _('Documentation appearance') }}">
<option value="system" selected>{{ _('System') }}</option>
<option value="light">{{ _('Light') }}</option>
<option value="dark">{{ _('Dark') }}</option>
</select>
</label>
</div>
<div class="navigation">
<nav class="nav-links" aria-label="{{ _('Primary') }}">
<a href="{{ zoite_site_url|e }}">{{ _('Home') }}</a>
<a href="{{ zoite_download_url|e }}">{{ _('Downloads') }}</a>
<a href="{{ zoite_addons_url|e }}">{{ _('Addons') }}</a>
<a href="{{ zoite_announcements_url|e }}">{{ _('Announcements') }}</a>
<a href="{{ pathto(docs_root)|e }}" aria-current="page">{{ _('Documentation') }}</a>
<a href="{{ zoite_atlas_url|e }}">{{ _('Atlas') }}</a>
<a href="{{ zoite_github_url|e }}" target="_blank" rel="noopener">{{ _('GitHub') }}</a>
<details class="nav-menu">
<summary>{{ _('IRC support') }}</summary>
<div class="nav-menu-panel" aria-label="{{ _('IRC support channels') }}">
<a href="{{ zoite_irc_url|e }}" target="_blank" rel="noopener"><strong>{{ _('Zoite IRC') }}</strong><span>{{ _('Official support') }} · #zoitechat</span></a>
<a href="{{ libera_irc_url|e }}" target="_blank" rel="noopener"><strong>{{ _('Libera.Chat') }}</strong><span>{{ _('Secondary support') }} · #zoitechat</span></a>
</div>
</details>
</nav>
</div>
</header>
{% endblock %}
{% block relbar1 %}{% endblock %}
{% block relbar2 %}{% endblock %}
{% block content %}
<div class="docs-layout{% if not render_sidebar %} no-sidebar{% endif %}">
{% if render_sidebar %}
<aside class="docs-sidebar" aria-label="{{ _('Documentation navigation') }}">
<details class="docs-navigation" id="docs-navigation" open>
<summary>{{ _('Documentation') }}</summary>
<div class="sidebar-content">
{% for sidebartemplate in sidebars %}
{% include sidebartemplate %}
{% endfor %}
</div>
</details>
</aside>
{% endif %}
<main class="docs-content" id="main-content" tabindex="-1">
<div class="docs-location">
<nav aria-label="{{ _('Breadcrumbs') }}">
<a href="{{ pathto(docs_root)|e }}">{{ _('Documentation') }}</a>
{% for parent in parents %}
<span aria-hidden="true">/</span> <a href="{{ parent.link|e }}">{{ parent.title }}</a>
{% endfor %}
{% if pagename != docs_root %}<span aria-hidden="true">/</span> <span>{{ title|striptags|e }}</span>{% endif %}
</nav>
<span class="docs-version">{{ release|e }}</span>
</div>
{% block body %}{% endblock %}
{% if prev or next %}
<nav class="page-relations" aria-label="{{ _('Related pages') }}">
<div>
{% if prev %}<small>{{ _('Previous') }}</small><a href="{{ prev.link|e }}" rel="prev">{{ prev.title }}</a>{% endif %}
</div>
<div>
{% if next %}<small>{{ _('Next') }}</small><a href="{{ next.link|e }}" rel="next">{{ next.title }}</a>{% endif %}
</div>
</nav>
{% endif %}
</main>
</div>
{% endblock %}
{% block footer %}
<footer class="site-footer">
<div class="footer-grid">
<p class="footer-copy">{{ project|e }} {{ _('documentation') }} · {{ release|e }}</p>
<nav class="footer-links" aria-label="{{ _('Footer navigation') }}">
<a href="{{ pathto(docs_root)|e }}">{{ _('Contents') }}</a>
<a href="{{ zoite_atlas_url|e }}">{{ _('Atlas') }}</a>
{% if hasdoc('search') %}<a href="{{ pathto('search')|e }}">{{ _('Search') }}</a>{% endif %}
{% if hasdoc('genindex') %}<a href="{{ pathto('genindex')|e }}">{{ _('Index') }}</a>{% endif %}
<a href="#top">{{ _('Back to top') }}</a>
</nav>
</div>
</footer>
{% endblock %}
+7
View File
@@ -0,0 +1,7 @@
{# SPDX-License-Identifier: GPL-2.0-or-later #}
{% if display_toc %}
<nav class="local-toc" aria-label="{{ _('On this page') }}">
<h2>{{ _('On this page') }}</h2>
{{ toc }}
</nav>
{% endif %}
+10
View File
@@ -0,0 +1,10 @@
{# SPDX-License-Identifier: GPL-2.0-or-later #}
{% if pagename != "search" %}
<form class="docs-search" action="{{ pathto('search')|e }}" method="get" role="search">
<label for="docs-search-input">{{ _('Search documentation') }}</label>
<div class="search-inputs">
<input id="docs-search-input" type="search" name="q" placeholder="{{ _('Search documentation') }}" autocomplete="off" spellcheck="false">
<button type="submit">{{ _('Search') }}</button>
</div>
</form>
{% endif %}
+7
View File
@@ -0,0 +1,7 @@
[theme]
inherit = basic
stylesheet = basic.css
pygments_style = sphinx
sidebars = searchbox.html, globaltoc.html, localtoc.html, sourcelink.html
[options]
+65
View File
@@ -0,0 +1,65 @@
Addons
======
ZoiteChat ships with a handful of useful plugins that extend the functionality of the client. In order to auto-load custom ones you just place them in an :file:`addons` subdir within your `config folder <settings.html#config-files>`_, manually with :command:`/load` and :command:`/unload`, or with :menuselection:`Window --> Plugins and Scripts`.
With the included scripting interfaces you can also use easier to create but equally powerful scripts in languages such as Python. These are loaded/unloaded in the same manner as plugins.
A directory of existing ZoiteChat addons can be found at `https://zoitechat.org/addons.php <https://zoitechat.org/addons.php>`_. In addition, since ZoiteChat is based on HexChat, most HexChat and many X-Chat 2 scripts and addons are compatible too, as long as they are written in a `supported scripting language <developers.html>`_.
Exec
----
The exec plugin provides the :command:`/exec` command for Windows. On Unix platforms there are built-in commands for this and this documentation does not apply to the behavior of those, see :command:`/help` for more information.
With Exec you can perform commands as if you ran them in the command line. The output will be printed at once, in the end of execution. If the command takes more than 10 seconds to complete, it gets aborted to avoid locking down ZoiteChat. Usage:
:command:`/exec ping google.com`
FISHLiM
-------
Adds FiSH encryption support. You set a password for a conversation/channel, and then all your messages get encrypted. Only those who know the password will be able to read your messages. Usage is simple: first you set the password:
:command:`/setkey yoursecretkey`
Then you let your fellow chatters know this password. Once they also set the password on their sides, they'll receive messages decrypted and send them encrypted, too.
You can remove the key with:
:command:`/delkey #channel`
Update Checker
--------------
Windows only. Automatically checks for available updates. Can also be manually checked via :menuselection:`Help --> Check for Updates` or by the command:
:command:`/updchk`
Sysinfo
-------
Prints out basic system information on both Windows and Unix. You can either activate in :menuselection:`Window --> Display System Info` or type:
:command:`/sysinfo`
Checksum
--------
Automatically calculates the SHA-256 checksum of files sent and recieved via DCC.
Winamp
------
Displays your currently playing song via :menuselection:`Window --> Display Current Song` or by command:
:command:`/winamp`
.. note::
- Windows-only and untested, but may work
- Foobar2000 can also be used with the `foo_winamp_spam <https://github.com/RazielZ/foo_winamp_spam>`_ plugin.
+119
View File
@@ -0,0 +1,119 @@
Appearance
==========
Theming
-------
Gtk Theme
~~~~~~~~~
Starting with version 2.18.0, ZoiteChat uses GTK 3. Theming is handled differently than it was in 2.17.x, HexChat, or X-Chat 2.
On Unix, the default is your system GTK theme. You can change the theme ZoiteChat uses in :menuselection:`Settings --> Preferences --> Appearance --> GTK3 theme`. Themes can be imported here too, which will only be available for ZoiteChat.
Themes installed via your system package manager or desktop environment control panel will also be available for ZoiteChat, as well as the rest of your GTK3 applications.
On Windows, the default theme is `Redstone <https://www.opendesktop.org/p/1013482/>`_ which is bundled with the ZoiteChat installer. In :menuselection:`Settings --> Preferences --> Appearance --> GTK3 theme`, you can import other themes as well, just like on Unix.
On both platforms, the theme selection drop down will show (user) for themes installed through ZoiteChat and (system) for system themes.
GTK 3 themes can be found on `openDesktop <https://www.opendesktop.org/browse?cat=135>`_ and other places like github. Any theme should work as long as it supports GTK 3.20 or higher.
Colors
~~~~~~
Colors are defined in :menuselection:`Settings --> Preferences --> Appearance --> Manage all client colors`. Text Colors set the palette for events to use. The rest like background color directly affect parts of the UI.
`mIRC <http://www.mirc.com/colors.html>`_ colors (00-15) are what you refer to when sending colored text over IRC for others to see and vice versa, because of this they should somewhat follow a set of standards so clients can agree 04 is red.
Local colors (16-31) can be changed to anything you wish, for instance to use in `text events <appearance.html#text-events>`_. However, ZoiteChat also supports the later `99 colors <https://modern.ircdocs.horse/formatting.html#colors-16-98>`_ standard so if you change the 16-31 colors it may conflict with that.
HexChat and X-Chat 2 supported color scheme "themes" as :file:`.hct` files, which are ZIP archives. Some of them are available from `HexChat Themes <https://hexchat.github.io/themes.html>`_, and you may have managed them with the discontinued Theme Manager.
If you want to use a HexChat/X-Chat theme, open the :file:`.hct` file in an application like 7-Zip or Ark and extract :file:`colors.conf`. Then go to :menuselection:`Settings --> Preferences --> Appearance --> Import colors.conf colors` to import it.
If the archive includes :file:`pevents.conf`, that can go in your `config folder <settings.html#config-files>`_ but may not work the same way it did in HexChat.
Text Events
~~~~~~~~~~~
Text events control the look of every event you see. They can be customized in :menuselection:`Settings --> Text Events` using these codes to format it:
- **%C<fg>,<bg>** Color code (e.g. %C02,00 is blue on grey)
- **%R** Reverse color
- **%U** Underlined text
- **%B** Bold text
- **%I** Italic text (2.10.0+)
- **%H** Hide text
- **%O** Normal text
- **%%** Escaped %
- **$t** Text separator (tab character)
- **$aXXX** Ascii value
- **$<num>** Event information
.. note::
Always hit enter after editing a field.
Icons
~~~~~
ZoiteChat comes with built in icons for the tray, user list, and channel tree (which can be disabled in Preferences). You can use custom icons by placing png icons (16x16 recommended) in an :file:`icons` subdir, which may need to be created, within your `config folder <settings.html#config-files>`_. The icons must be named exactly as follows including file extensions:
- User List
- ulist_netop.png
- ulist_founder.png
- ulist_owner.png
- ulist_op.png
- ulist_halfop.png
- ulist_voice.png
- Channel Tree
- tree_channel.png
- tree_dialog.png
- tree_server.png
- tree_util.png
- Tray Icon
- tray_normal.png
- tray_fileoffer.png
- tray_highlight.png
- tray_message.png
- zoitechat.png
Buttons, Menus, and Popups
--------------------------
Userlist Popup
~~~~~~~~~~~~~~
Popups are shown when you right click on a nickname, either in the userlist or in the main chat itself. These can be edited in :menuselection:`Settings --> Userlist Popup`
The Name column can take either just the name of the entry, *SUB*/*ENDSUB* for submenus, *SEP* for separators, and *TOGGLE* for toggleable options.
Prefix a character with *_* for keyboard shortcuts (e.g. N_ame will bind a).
The Command column can take any `command <commands.html>`_ with text formatted using the same codes as `text events <appearance.html#text-events>`_ and on top of that they also have their own codes:
- **%a** all selected nicks
- **%c** current channel
- **%h** selected nick's hostname
- **%m** machine info
- **%n** your nickname
- **%s** selected nickname
- **%t** time/date
- **%u** selected nick's account
The **gui_ulist_doubleclick** setting can run a command using these codes when double-clicking a nick in the userlist.
Userlist Buttons
~~~~~~~~~~~~~~~~
Buttons are shown below the userlist, can be edited in :menuselection:`Settings --> Userlist Buttons`, and take the same syntax as Userlist Popups.
Usermenu
~~~~~~~~
In order to add custom entries to your menu you need to first enable the usermenu with the command :command:`/set gui_usermenu on` which may require a restart. Once this is enabled you can go to :menuselection:`Usermenu --> Edit this Menu` to add any `command <commands.html>`_ you would like. For menu entries it supports the same syntax as Userlist Popups.
+73
View File
@@ -0,0 +1,73 @@
#!/usr/bin/env python3
import os
import shutil
import subprocess
import sys
def prune_unused_local_assets(output_dir):
index_path = os.path.join(output_dir, "index.html")
try:
with open(index_path, "r", encoding="utf-8") as index_file:
index_html = index_file.read()
except OSError:
return
if "bootswatch-" not in index_html:
for bootstrap_version in ("bootstrap2", "bootstrap3"):
css_dir = os.path.join(
output_dir, "_static", "css", bootstrap_version
)
if not os.path.isdir(css_dir):
continue
for filename in os.listdir(css_dir):
if filename.startswith("bootswatch-") and filename.endswith(".css"):
os.remove(os.path.join(css_dir, filename))
if "jquery.cookie.min.js" not in index_html:
js_dir = os.path.join(output_dir, "_static", "js")
for filename in ("jquery.cookie.min.js", "jquery.cookie-1.4.1.min.js"):
path = os.path.join(js_dir, filename)
if os.path.isfile(path):
os.remove(path)
def main():
if len(sys.argv) != 4:
raise SystemExit("usage: build-docs.py SOURCE_DIR OUTPUT_DIR STAMP_FILE")
source_dir = os.path.abspath(sys.argv[1])
output_dir = os.path.abspath(sys.argv[2])
stamp_file = os.path.abspath(sys.argv[3])
doctree_dir = output_dir + ".doctrees"
shutil.rmtree(output_dir, ignore_errors=True)
shutil.rmtree(doctree_dir, ignore_errors=True)
os.makedirs(output_dir, exist_ok=True)
subprocess.run(
[
sys.executable,
"-m",
"sphinx",
"-b",
"html",
"-d",
doctree_dir,
source_dir,
output_dir,
],
check=True,
)
prune_unused_local_assets(output_dir)
with open(stamp_file, "w", encoding="utf-8") as stamp:
stamp.write("built\n")
if __name__ == "__main__":
main()
+110
View File
@@ -0,0 +1,110 @@
Building
========
Windows
-------
The Windows build is currently x64-only and follows the same shape as
ZoiteChat's Windows GitHub Actions workflow: prepare ``C:\gtk-build``, put
the dependency bundles where ``win32\zoitechat.props`` expects them, then build
``win32\zoitechat.sln`` with MSBuild.
Software
~~~~~~~~
Install:
- `Visual Studio 2022 Community <https://visualstudio.microsoft.com/vs/community/>`_ or `Visual Studio 2022 Build Tools <https://visualstudio.microsoft.com/downloads/>`_ with the **Desktop development with C++** workload
- `Git for Windows <https://git-scm.com/download/win>`_
- `7-Zip <https://7-zip.org/>`_
- `Python 3.14 x64 <https://www.python.org/downloads/windows/>`_
- `Inno Setup 6 <https://jrsoftware.org/isdl.php>`_ if you want to build the installer
Source code
~~~~~~~~~~~
Clone the source and enter the checkout::
git clone https://github.com/zoitechat/zoitechat.git
cd zoitechat
Dependencies
~~~~~~~~~~~~
Create the dependency root::
mkdir C:\gtk-build
Download the `GTK3 x64 bundle <https://github.com/ZoiteChat/gvsbuild/releases/download/zoitechat-2.18.1/GTK3_Gvsbuild_zoitechat-2.18.1_x64.zip>`_
and extract it to ``C:\gtk-build\gtk\x64\release``.
Download the remaining Windows dependency artifacts from the
`ZoiteChat gvsbuild releases <https://github.com/zoitechat/gvsbuild/releases>`_
and extract them to these paths:
- gendef: ``C:\gtk-build\gendef``
- WinSparkle x64 bundle: ``C:\gtk-build\WinSparkle\x64``
- Perl 5.42 x64 bundle: ``C:\gtk-build\perl-5.42.0.1\x64``
Set up Python where the project file expects it. From PowerShell, adjust the
Python path if your install is elsewhere::
mkdir C:\gtk-build\python-3.14
cmd /c mklink /J C:\gtk-build\python-3.14\x64 C:\Python314
C:\gtk-build\python-3.14\x64\python.exe -m pip install --upgrade pip cffi
If you use different locations, set these environment variables before building
instead of editing ``win32\zoitechat.props``:
- ``ZOITECHAT_DEPS_PATH``
- ``ZOITECHAT_GENDEF_PATH``
- ``ZOITECHAT_PERL_PATH``
- ``ZOITECHAT_PYTHON3_PATH``
- ``ZOITECHAT_WINSPARKLE_PATH``
- ``ZOITECHAT_ISCC_PATH``
Building
~~~~~~~~
Open **Developer Command Prompt for VS 2022** in the source checkout and run::
msbuild win32\zoitechat.sln /m /verbosity:minimal /p:Configuration=Release /p:Platform=x64
To build the installer, open ``win32\zoitechat.sln`` in Visual Studio,
set ``release/installer`` as the startup project, then run **Build**.
The build output is written to ``..\zoitechat-build\x64``. The installer
build writes a ``ZoiteChat*.exe`` installer in that output tree.
Unix
----
First of all, you have to install the build dependencies. Package names
differ across distros, so be creative and check meson's output
if you get an error.
Most package-managers can get the dependencies for you:
- dnf::
dnf install -y meson gettext-devel gtk3-devel iso-codes-devel libarchive-devel libayatana-appindicator-gtk3-devel libcanberra-devel lua-devel openssl-devel perl-devel perl-ExtUtils-Embed python3-cffi python3-devel
- apt::
apt-get install -y meson gettext iso-codes libarchive-dev libayatana-appindicator3-dev libcanberra-dev libglib2.0-dev libgtk-3-dev liblua5.4-dev libpci-dev libperl-dev libssl-dev python3-dev python3-cffi desktop-file-utils
ZoiteChat has its source code hosted using `Git <http://git-scm.com/>`_, so you have to install Git as
well. When it's ready, you can start the actual compilation, which is
basically:
.. code-block:: bash
git clone https://github.com/zoitechat/zoitechat.git
cd zoitechat
meson setup build
ninja -C build
sudo ninja -C build install
This will compile with defaults. See ``meson configure build`` for more info
about flags.
+1294
View File
File diff suppressed because it is too large. Load diff
+33
View File
@@ -0,0 +1,33 @@
Commands
========
Commands in ZoiteChat are prefixed with **/** and to escape them you can type it twice e.g. **//**
ZoiteChat will first try to run plugin commands, then user commands, then client commands, and lastly send it directly to the server.
User Commands
-------------
User commands can be used to create aliases, to run multiple commands at a time, or more complex custom commands. They are set in :menuselection:`Settings --> User Commands`.
An alias is just a shortcut refering to an existing command, for example */j* refers to */join &2*
Naming two user commands the same thing will run both in the order they are listed.
For more complex commands you can use these codes:
- **%c** Current channel
- **%e** Current network
- **%m** Machine info
- **%n** Your nick
- **%t** Time/date
- **%v** ZoiteChat version
- **%<num>** Word
- **&<num>** Word from end of line
.. _commands_list:
List of Commands
----------------
To get a full list of commands and what they do type :command:`/help -l`.
+300
View File
@@ -0,0 +1,300 @@
# -*- coding: utf-8 -*-
#
# ZoiteChat documentation build configuration file, created by
# sphinx-quickstart on Thu Jan 03 18:13:28 2013.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys
import os
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.insert(0, os.path.abspath('.'))
# -- General configuration ----------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
needs_sphinx = '1.8'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = []
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.rst'
# The encoding of source files.
source_encoding = 'utf-8'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'ZoiteChat'
copyright = u'2026, TingPing'
# Read the release directly from the repository's VERSION file so the
# documentation always matches the ZoiteChat source/release being built.
_version_file = os.path.abspath(
os.path.join(os.path.dirname(__file__), '..', 'VERSION')
)
with open(_version_file, encoding='utf-8') as version_file:
release = version_file.read().strip()
# The short X.Y version used by Sphinx.
version = '.'.join(release.split('.')[:2])
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = 'en'
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build']
# The reST default role (used for this markup: `text`) to use for all documents
#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
# Trim spaces before footnote references that are necessary for the reST
# parser to recognize the footnote, but do not look too nice in the output.
trim_footnote_reference_space = True
# -- Options for HTML output --------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'zoitechat'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#html_theme_options = {'bootswatch_theme': "united",}
# Add any paths that contain custom themes here, relative to this directory.
html_theme_path = ['_theme']
# RTD_NEW_THEME = True
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
#html_title = 'ZoiteChat project'
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# A dictionary of values to pass into the template engine’s context for all
# pages. Single values can also be put in this dictionary using the -A command-line
# option of sphinx-build.
#html_context = { 'foo': 'bar' }
html_context = {
'zoite_site_url': 'https://zoitechat.org/',
'zoite_download_url': 'https://zoitechat.org/download.php',
'zoite_addons_url': 'https://zoitechat.org/addons.php',
'zoite_announcements_url': 'https://zoitechat.org/announcements.php',
'zoite_github_url': 'https://github.com/ZoiteChat/zoitechat',
'zoite_atlas_url': 'https://atlas.zoitechat.org/',
'zoite_irc_url': 'https://zoite.net/webchat/?join=%23zoitechat',
'libera_irc_url': 'https://web.libera.chat/#zoitechat',
}
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
html_logo = '_static/zoitechat-logo.svg'
# Use the ZoiteChat logo instead of the old HexChat favicon.
html_favicon = '_static/zoitechat-logo.svg'
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# Local, readable assets shared by hosted and offline HTML documentation.
html_css_files = ['css/zoitechat-docs.css', 'css/zoitechat-site.css']
html_js_files = ['js/zoitechat-docs.js']
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
html_last_updated_fmt = '%b %d %Y, %H:%M:%S'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
html_use_smartypants = True
# Sphinx will add "permalinks" for each heading and description environment
# as paragraph signs that become visible when the mouse hovers over them.
# Changed in version 1.1 to take the text of the permalink, but defaults
# to paragraph symbol.
#html_add_permalinks = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# If false, no module index is generated.
#html_domain_indices = True
# If false, no index is generated.
#html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# If true, the reST sources are included in the HTML build as _sources/name.
# The default is True.
#
# Warning
# If this config value is set to False, the JavaScript search function will
# only display the titles of matching documents, and no excerpt from the
# matching contents.
html_copy_source = True
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
html_file_suffix = '.html'
# Suffix for generated links to HTML files.
# The default is whatever html_file_suffix is set to;
# it can be set differently (e.g. to support different web server setups).
#html_link_suffix = '.html'
# A string with the fully-qualified name of a HTML Translator class,
# that is, a subclass of Sphinx’ HTMLTranslator, that is used to translate
# document trees to HTML. Default is None (use the builtin translator).
#html_translator_class
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
html_show_copyright = False
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
html_show_sphinx = False
# Output file base name for HTML help builder.
htmlhelp_basename = 'ZoiteChatdoc'
# -- Options for LaTeX output --------------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#'preamble': '',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'ZoiteChat.tex', u'ZoiteChat Documentation',
u'TingPing', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# If true, show page references after internal links.
#latex_show_pagerefs = False
# If true, show URL addresses after external links.
#latex_show_urls = False
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# If false, no module index is generated.
#latex_domain_indices = True
# -- Options for manual page output --------------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'zoitechat', u'ZoiteChat Documentation',
[u'TingPing'], 1)
]
# If true, show URL addresses after external links.
#man_show_urls = False
# -- Options for Texinfo output ------------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
('index', 'ZoiteChat', u'ZoiteChat Documentation',
u'TingPing', 'ZoiteChat', 'One line description of project.',
'Miscellaneous'),
]
# Documents to append as an appendix to all manuals.
#texinfo_appendices = []
# If false, no module index is generated.
#texinfo_domain_indices = True
# How to display URL addresses: 'footnote', 'no', or 'inline'.
#texinfo_show_urls = 'footnote'
# -- Translations ---------------------------------
locale_dirs = ['po']
gettext_compact = False
+27
View File
@@ -0,0 +1,27 @@
Developers
==========
.. toctree::
:maxdepth: 2
building
plugins
Scripting
---------
ZoiteChat currently supports scripting with Python 3, Perl, and Lua, with Lua being the recommended option.
.. toctree::
:maxdepth: 1
:glob:
script*
Building Perl modules on Windows
--------------------------------
.. toctree::
:maxdepth: 2
perl_modules
+386
View File
@@ -0,0 +1,386 @@
Frequently Asked Questions
==========================
How do I migrate my settings from HexChat?
----------------------------------------
.. Caution:: Not officially supported; ZoiteChat 2.19.0 should be backwards compatible with HexChat 2.16.x, but double-check your settings and addons after migrating.
- Unix
1. Copy ``~/.config/hexchat`` to ``~/.config/zoitechat``. If there is nothing in ``~/.config/zoitechat``, check your `config file location <settings.html#config-files>`_. Some installation methods like snap and flatpak use a different directory.
2. Rename ``~/.config/zoitechat/hexchat.conf`` to ``~/.config/zoitechat/zoitechat.conf``
3. Double-check your 3rd party addons (plugins/scripts) in ``~/.config/zoitechat/addons``
4. Double-check your client certs in ``~/.config/zoitechat/certs``
- Windows
1. Copy ``%APPDATA%\HexChat`` to ``%APPDATA%\ZoiteChat``
2. Rename ``%APPDATA%\HexChat\hexchat.conf`` to ``%APPDATA%\ZoiteChat\zoitechat.conf``
3. Move all your 3rd party addons (plugins/scripts) to ``%APPDATA%\ZoiteChat\addons``
4. Move all your client certs to ``%APPDATA%\ZoiteChat\certs``
How do I autoconnect and join a channel on start?
--------------------------------------------------
In the Network List, select the network to which you would like to automatically connect to at startup,
click the "Edit..." button, and then tick the checkbox for "Connect to this network automatically".
To automatically join channels for this network upon connecting, select the "Autojoin channels"
tab in this same window and add your desired channels here. You can also add a currently joined channel
by right-clicking its name in the channel switcher and by toggling "Autojoin" on.
How do I auto-reconnect after my computer wakes up from sleep/hibernate mode?
--------------------------------------------------------------------------------------
Try the following command from a chat window (time is in seconds)::
/set net_ping_timeout 60
Why are channels joined before identifying?
-------------------------------------------
There are 3 ways to authenticate before joining a channel, all are network dependant but nickserv is common and SASL is the best (ZoiteChats default network list tries to use the best method by default, don't change it):
- Use SASL which can be enabled in :menuselection:`ZoiteChat --> Network list --> Edit`.
Note that your username must match your nickserv account, you can set it by unchecking *Use global user information*.
- Use a `client certificate <tips.html#client-certificates>`_.
- Use a Nickserv password and increase the delay before joining in :menuselection:`Settings --> Preferences --> Advanced`. This is not recommended but may be the only option on some older networks.
Some networks (e.g, Libera.chat) also allow you to pass your nickserv password as part of a server login (Login method: Server password (/PASS password)), which ensures Nickserv identification is complete before zoitechat joins channels. This method is less secure than SASL because the verbatim password is transferred during login.
How do I change what browser is opened?
---------------------------------------
- Windows:
:menuselection:`Control Panel --> Default Programs`
- Unix:
- Gnome: :menuselection:`Settings --> Apps --> Default Apps`
- Plasma: :menuselection:`System Settings --> Apps & Windows --> Default Applications`
- Other DEs should have their own settings in a similar location.
If these do not work or you do not use a DE use the command :command:`gio`::
gio mime x-scheme-handler/http firefox.desktop
gio mime x-scheme-handler/https firefox.desktop
Upon launching it will use the *Exec* line in the desktop file replacing *%u* with the url.
If you get a blank window this is where the problem is.
Alternatively you can add a `Url Handler <settings.html#url-handlers>`_.
How do I connect through a proxy?
---------------------------------
Go to the menus, :menuselection:`Settings --> Preferences --> Network Setup`
and fill in the requested information there. Authentication (using a
username and password) is only supported for HTTP and Socks5.
For information on Tor see our `tips page <tips.html#tor>`_.
How do I show @ and + in front of nicknames that are Op and Voice when they talk?
---------------------------------------------------------------------------------
To display @ and + characters next to nicknames as they talk, do the
following:
In the menus, open up :menuselection:`Settings --> Text Events`. Find the *Channel
Message* event in the list. The $3 code can be inserted to print the
user's mode-character (e.g. @ or +). For example, you might want to
change the default::
%C18%H<%H$4$1%H>%H%O$t$2
to::
%C18%H<%H$4$3$1%H>%H%O$t$2
Don't forget to **press Enter**, so the changes take effect in the list
at the top of the window.
How do I set different ban types?
---------------------------------
1. Right click the nickname in the userlist, and choose a ban type from
the "Kick/Ban" submenu.
2. You can also do it manually: > /ban nick bantype where the bantype is
a number from 0 to 3.
3. Or set the default with:
/set irc\_ban\_type bantype sets the default ban type to use for
all bans. The different types are::
0 = *!*@*.host
1 = *!*@domain
2 = *!*user@*.host
3 = *!*user@domain
Why does the timestamp overlap some nicknames?
----------------------------------------------
Some networks allow very long nicknames (up to 32 letters). It can be
annoying to have the separator bar move too far to the right, just for
one long nick. Therefore, it has a set limit for the distance it will
move to the right. If you use a large font, you may need to adjust this
distance. It is set in pixels, for example::
/set text_max_indent 320
Once you adjust this setting high enough, overlapping timestamps and
nicknames should not occur. The adjustment will not take effect
immediately, a restart may be needed.
How do I hide join and part messages?
-------------------------------------
To disable joins and parts from being displayed in all channels check 'Hide join and part messages' under
:menuselection:`Settings -> Preferences -> Chatting -> General`.
Then all channels you join **after** setting this will start with "Show
join/part messages" turned off.
To disable the prints for only certain channels, right click on the channel tab and click
:menuselection:`Settings -> Hide Join/Part Messages`.
There are also addons to help with this, for instance `TingPing's Smart Parts script <https://github.com/TingPing/plugins/blob/master/HexChat/smartparts.lua>`_ which only shows join and part messages from active users.
Why doesn't DCC send work behind a router?
------------------------------------------
If you are behind a IP-NAT or ADSL router, you will most likely have an
address like 192.168.0.1. This address is not usable on the Internet,
and must be translated.
When offering a DCC file, ZoiteChat will tell the receiver your address.
If it says 192.168.0.1, the receiver will not be able to connect. One
way to make it send your "real" address is to enable the "Get my IP from
IRC Server" option in ZoiteChat. This option is available in :menuselection:`Settings --> Preferences -->
Network setup`. When you turn it ON, you will have to re-login
to the server before it'll take effect.
You will also need to forward some ports for use in DCC send. You may
pick almost any port range you wish, for example, in ZoiteChat set:
First DCC send port: 4990
Last DCC send port: 5000
This will allow you to send up to ten files at the same time, which
should be plenty for most people. Lastly, configure your router/modem to
forward ports 4990-5000 to your PC's address. You'll have to consult
your router/modem's manual on how to do this.
How do I execute multiple commands in one line?
-----------------------------------------------
There are a few ways to do this:
- If this is during connection the network list (Ctrl+s) has a 'connect commands'
section as well as most login types you would need.
- /LOAD -e <textfile>, where <textfile> is a file in your config dir
containing commands on each line.
- Separate your commands with CTRL-SHIFT-u-a. This will appear as a
little box with numbers on it (or an invisible character).
- You can create two UserCommands, with the same name, and then execute
the UserCommand. It will be executed in the same order as it's
written in the UserCommands GUI.
I get this error: "Unknown file type abc.yz. Maybe you need to install the Perl or Python plugin?"
--------------------------------------------------------------------------------------------------
If you get this error when trying to load a Perl or Python script, it
means the plugin or language for running those scripts isn't loaded.
- On Unix install the packages including these plugins (e.g. zoitechat-perl) and perl.
- On Windows select the plugins in the installer.
.. note::
ZoiteChat on Windows is tied to specific versions of Python, Perl, and Lua at compile time.
If you already have the script runtime installed and you still see this error, you most likely
need to install an older version. This should be listed in the `changelog <changelog.html>`_ or
come to **#zoitechat** on Libera.Chat or Zoite and ask!
How do I play sound files on certain events?
--------------------------------------------
In the menus, go to: :menuselection:`Settings --> Preferences --> Sound`.
Select the event you want to make a sound on, then type in a sound
filename (or use the Browse button). On Windows, sound files must be in .wav format.
How do I auto-load scripts at startup?
--------------------------------------
The root of your ZoiteChat config is:
- Unix::
~/.config/zoitechat
- Windows::
%APPDATA%\\ZoiteChat
Referred to as ``<config>`` from now. ZoiteChat automatically loads, at startup:
- ``<config>/addons/\*.pl`` Perl scripts
- ``<config>/addons/\*.py`` Python scripts
- ``<config>/addons/\*.lua`` Lua scripts
- ``<config>/addons/\*.dll`` Plugins (Windows)
- ``<config>/addons/\*.so`` Plugins (Unix)
How do I minimize ZoiteChat to the System Tray (Notification Area)?
-----------------------------------------------------------------
ZoiteChat includes support for a tray icon which can be enabled in
:menuselection:`Settings --> Preferences --> Alerts`.
If that option does not appear then ZoiteChat could not detect a usable tray.
How do I start ZoiteChat with...?
-------------------------------
To see the various launch options such as setting configdir or minimize level run::
zoitechat --help
Where are the log files saved to?
---------------------------------
- Unix::
~/.config/zoitechat/logs
- Windows::
%APPDATA%\ZoiteChat\logs
Why do config files and logs appear as a single line?
-----------------------------------------------------
ZoiteChat supports many platforms and to be consistent it uses
`Unix line endings <https://en.wikipedia.org/wiki/Newline>`_.
This is normally not a problem at all but Microsoft Notepad does not
support these so they are rendered as a single line. Any other editor should support them.
How do I rotate log files every so often?
-----------------------------------------
By default settings, no rotation occurs, your log files will just keep
getting larger.
Go to :menuselection:`Settings --> Preferences --> Logging` and change the
log filename to any one of these::
%Y-%m-%d/%n-%c.log -> 2026-12-30/Libera.Chat-#channel.log
%n/%Y-%m-%d/%c.log -> Libera.Chat/2026-12-30/#channel.log
%n/%c.log -> Libera.Chat/#channel.log (no rotation)
%Y, %m and %d represents the current year, month and day respectively.
%n is the network name, e.g. "Libera.Chat" or "Zoite", and finally, %c
is the channel. In these examples, a new log filename and folder would
be created after midnight.
The format can also be a full path if you want to save logs to an external drive for example.
For the full list of formatting codes, please refer to the
`Unix <http://linux.die.net/man/3/strftime>`_ or
`Windows <http://msdn.microsoft.com/en-us/library/fe06s4ak(v=vs.110).aspx#languageReferenceRemarksToggle>`_
documentation on `strftime`.
Where did the Real Name field go?
---------------------------------
The Real name field used to be accessible via the Network List, which is the
very first screen that a new user sees. Newcomers, who are not familiar with
IRC terminology, might be afraid of their personal data. In order to avoid
alienating such people, we decided to remove this setting from the Network
List. Now you can access this setting under :menuselection:`Settings -->
Preferences --> Advanced` instead, or if you prefer the command line, you can
use the following command::
/set irc_real_name Stewie Griffin
Why don't beep sound alerts work?
---------------------------------
On Windows, ZoiteChat is using the `Instant Message Notification` system sound
for making beep alerts, and if it's unspecified, it attempts to produce a
simple beep effect. In case you don't hear beeps when alerts occur, you need
to set this system sound to the desired sound effect. To do this, go to
:menuselection:`Control Panel --> Hardware and Sound --> Change system sounds`.
On Unix it uses libcanberra by default to play the "message-new-instant" freedesktop sound.
Use the *canberra-gtk-play* command to test playing events or custom sound files
as that is likely where the problem lies.
.. image:: _static/img/faq_sound.png
How do I type Unicode characters?
---------------------------------
Press `Ctrl + Shift + U` at once. When you release the keys, `u` will appear in
your input box.
.. image:: _static/img/faq_unicode_1.png
Now you can enter the 4-digit code of the desired glyph. When you're done, just
press `Space` or `Return`, and the glyph will appear as well.
.. image:: _static/img/faq_unicode_2.png
.. Note:: Windows alt codes will not work.
The input box now also has a unicode selector, visible on the right side as a |face| (smile face) emoji.
.. |face| unicode:: U+263B
How do I add a network to the official list?
--------------------------------------------
There are no official requirements to be added to the network list,
but there are some simple guidelines of what the network SHOULD have.
- A maintained website listing servers.
- Has been around for a while.
- A decent amount of active users (100+).
- Doesn't block large groups of users.
You should make a pull request on `GitHub <https://github.com/zoitechat/zoitechat/pulls>`_ if you
want the quickest results, the list is stored in *src/common/servlist.c*. You should include an
SSL server if supported but you should not include IPv6 only servers and mark SASL as default if supported.
Follow the syntax of other networks in the file.
A portable install fails with "Missing VCRUNTIME140.dll"
--------------------------------------------------------
ZoiteChat, being a C application, depends on Microsoft's Visual C++ Runtime. Normally the ZoiteChat installer will
install this runtime for you and function as expected. However, if you use the portable mode of the installer
it cannot install system libraries (this allows running as an unprivileged user).
You can simply install it from Microsoft directly to solve this error:
- 32bit - https://aka.ms/vs/17/release/vc_redist.x86.exe
- 64bit - https://aka.ms/vs/17/release/vc_redist.x64.exe
What does the Safe Mode start menu entry do?
--------------------------------------------
This launches ZoiteChat with the ``--no-auto`` and ``--no-plugins`` options. This can bypass any issues you have
with a problematic plugin or auto-connecting network.
+69
View File
@@ -0,0 +1,69 @@
Getting Started
===============
Quick Start
-----------
The first time you start ZoiteChat you'll see the *Network List* as seen
below:
.. image:: _static/img/network_list.png
Here you can specify your global logon details. The *Nick name* will be
your name visible in IRC channels (or second/third choice if it's
already taken), and the *User name* is how you identify yourself to the
server.
You can pick a server from the default list, or if it's not there, you
can add your own by clicking the *Add* button:
.. image:: _static/img/network_list_add.png
After you named it, click *Edit...* and specify the details of your
connection. You need at least the following information to join to a
certain group of people:
- server address
- server port
- channel name
You can see an example below:
.. image:: _static/img/network_list_edit.png
In this example, the server address is *irc.foo.bar* and the port
is *6667*. The channel of choice is *#lobby*. Favorite channels are
joined to automatically upon connecting to the network. After you
finished editing, click *Close* to return to the *Network List*.
Now select the network you want to connect to and click *Connect*. After
a successful connection you'll see the following window:
.. image:: _static/img/connection_complete_msg.png
If you don't want to join a channel just yet, leave it as it is. If you
know the channel name already, specify it with the second option. In
case you want to browse through the channel list, select the third
option. Then press *OK* to close this window.
After you successfully joined a channel, you should see something like
this:
.. image:: _static/img/channel_window.png
That's it, you're online. Now you can learn more about ZoiteChat and
customize it for your needs. This website is a good starting point, but
you can find a lot more on the net. Have fun!
Frequently Asked Questions
--------------------------
.. toctree::
:maxdepth: 2
faq
Changelog
---------
.. toctree::
:maxdepth: 2
changelog
+35
View File
@@ -0,0 +1,35 @@
How to Help
===========
Documentation
-------------
For simple edits of the documentation just go to the page and click *Show/Edit on Github* on the left side, fork the repo, edit it, and submit a pull request.
For more complex additions I direct you to documentation for the projects we use:
- reStructuredText_ for the markup language.
- Pandoc_ for converting existing docs.
- Github_ and ReadTheDocs_ for hosting.
- Sphinx_ for generating the docs.
.. _Documentation: https://github.com/zoitechat/documentation
.. _reStructuredText: http://docutils.sourceforge.net/rst.html
.. _Sphinx: http://sphinx-doc.org/
.. _ReadTheDocs: http://read-the-docs.readthedocs.org/en/latest/
.. _Github: https://help.github.com/
.. _Pandoc: http://johnmacfarlane.net/pandoc/README.html
Bug Reports
-----------
We use `Github Issues <https://github.com/zoitechat/zoitechat/issues>`_ for our bug reports. Be sure to search on there for similar issues before posting your own, if it already exists pointing out you also have the issue never hurts. With the bug report include at minimum the information from *help -> about*.
.. note::
Issues is not a forum for asking questions, please direct those to IRC for now.
IRC Support
-----------
Anybody who enjoys helping others and understands much of what's on this site is welcome to help new users in either of our official irc channels: on Zoite, `#zoitechat <ircs://irc.zoite.net/zoitechat>`_ or on Libera.Chat, `#zoitechat <ircs://irc.libera.chat/zoitechat>`_.
+21
View File
@@ -0,0 +1,21 @@
Client Documentation
--------------------
.. toctree::
:maxdepth: 2
getting_started
settings
commands
appearance
addons
tips
Contributor Documentation
-------------------------
.. toctree::
:maxdepth: 2
helping
developers
+36
View File
@@ -0,0 +1,36 @@
#!/usr/bin/env python3
import os
import shutil
import sys
def main():
if len(sys.argv) != 4:
raise SystemExit("usage: install-docs.py STAMP_FILE SOURCE_DIR DATADIR")
_, source_dir, datadir = sys.argv[1:]
source_dir = os.path.abspath(source_dir)
if not os.path.isfile(sys.argv[1]):
raise SystemExit("documentation build did not produce its stamp file")
if not os.path.isfile(os.path.join(source_dir, "index.html")):
raise SystemExit("documentation build did not produce index.html")
if os.path.isabs(datadir):
destdir = os.environ.get("DESTDIR", "")
if destdir:
data_root = os.path.join(destdir, datadir.lstrip("/\\"))
else:
data_root = datadir
else:
data_root = os.path.join(os.environ["MESON_INSTALL_DESTDIR_PREFIX"], datadir)
destination = os.path.join(data_root, "doc", "zoitechat", "html")
shutil.rmtree(destination, ignore_errors=True)
os.makedirs(os.path.dirname(destination), exist_ok=True)
shutil.copytree(source_dir, destination)
if __name__ == "__main__":
main()
+198
View File
@@ -0,0 +1,198 @@
@ECHO OFF
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set BUILDDIR=_build
set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
set I18NSPHINXOPTS=%SPHINXOPTS% .
if NOT "%PAPER%" == "" (
set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
)
if "%1" == "" goto help
if "%1" == "help" (
:help
echo Please use `make ^<target^>` where ^<target^> is one of
echo html to make standalone HTML files
echo dirhtml to make HTML files named index.html in directories
echo singlehtml to make a single large HTML file
echo pickle to make pickle files
echo json to make JSON files
echo htmlhelp to make HTML files and a HTML help project
echo qthelp to make HTML files and a qthelp project
echo devhelp to make HTML files and a Devhelp project
echo epub to make an epub
echo latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
echo text to make text files
echo man to make manual pages
echo texinfo to make Texinfo files
echo gettext to make PO message catalogs
echo changes to make an overview over all changed/added/deprecated items
echo linkcheck to check all external links for integrity
echo doctest to run all doctests embedded in the documentation if enabled
echo server to run the Python server
goto end
)
if "%1" == "clean" (
if exist "%BUILDDIR%" rmdir /q /s "%BUILDDIR%"
goto end
)
if "%1" == "html" (
%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
if errorlevel 1 exit /b 1
echo.
echo Build finished. The HTML pages are in %BUILDDIR%/html.
goto end
)
if "%1" == "dirhtml" (
%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
if errorlevel 1 exit /b 1
echo.
echo Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
goto end
)
if "%1" == "singlehtml" (
%SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
if errorlevel 1 exit /b 1
echo.
echo Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
goto end
)
if "%1" == "pickle" (
%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
if errorlevel 1 exit /b 1
echo.
echo Build finished; now you can process the pickle files.
goto end
)
if "%1" == "json" (
%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
if errorlevel 1 exit /b 1
echo.
echo Build finished; now you can process the JSON files.
goto end
)
if "%1" == "htmlhelp" (
%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
if errorlevel 1 exit /b 1
echo.
echo Build finished; now you can run HTML Help Workshop with the ^
.hhp project file in %BUILDDIR%/htmlhelp.
goto end
)
if "%1" == "qthelp" (
%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
if errorlevel 1 exit /b 1
echo.
echo Build finished; now you can run "qcollectiongenerator" with the ^
.qhcp project file in %BUILDDIR%/qthelp, like this:
echo ^> qcollectiongenerator %BUILDDIR%\qthelp\ZoiteChat.qhcp
echo To view the help file:
echo ^> assistant -collectionFile %BUILDDIR%\qthelp\ZoiteChat.ghc
goto end
)
if "%1" == "devhelp" (
%SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
if errorlevel 1 exit /b 1
echo.
echo Build finished.
goto end
)
if "%1" == "epub" (
%SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
if errorlevel 1 exit /b 1
echo.
echo Build finished. The epub file is in %BUILDDIR%/epub.
goto end
)
if "%1" == "latex" (
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
if errorlevel 1 exit /b 1
echo.
echo Build finished; the LaTeX files are in %BUILDDIR%/latex.
goto end
)
if "%1" == "text" (
%SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
if errorlevel 1 exit /b 1
echo.
echo Build finished. The text files are in %BUILDDIR%/text.
goto end
)
if "%1" == "man" (
%SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
if errorlevel 1 exit /b 1
echo.
echo Build finished. The manual pages are in %BUILDDIR%/man.
goto end
)
if "%1" == "texinfo" (
%SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
if errorlevel 1 exit /b 1
echo.
echo Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
goto end
)
if "%1" == "gettext" (
%SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
if errorlevel 1 exit /b 1
echo.
echo Build finished. The message catalogs are in %BUILDDIR%/locale.
goto end
)
if "%1" == "changes" (
%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
if errorlevel 1 exit /b 1
echo.
echo The overview file is in %BUILDDIR%/changes.
goto end
)
if "%1" == "linkcheck" (
%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
if errorlevel 1 exit /b 1
echo.
echo Link check complete; look for any errors in the above output ^
or in %BUILDDIR%/linkcheck/output.txt.
goto end
)
if "%1" == "doctest" (
%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
if errorlevel 1 exit /b 1
echo.
echo Testing of doctests in the sources finished, look at the ^
results in %BUILDDIR%/doctest/output.txt.
goto end
)
if "%1" == "server" (
cd %BUILDDIR%/html
if errorlevel 1 exit /b 1
echo.
python -u -m SimpleHTTPServer
goto end
)
:end
+121
View File
@@ -0,0 +1,121 @@
Building Perl modules on Windows
================================
.. note:: These instructions are untested, and kept here for reference only. If you have the knowledge to update this page, patches are welcome!
CPAN
----
Software
--------
- `Visual Studio 2012 Express for Windows Desktop`_ + `Visual Studio 2012 Update 3`_
- Perl `x86`_ or `x64`_
- `MSYS`_ (I'm linking to this version but you can use ANY MSYS, it's possible to use MSYS from MozillaBuild but it's really ancient)
- This `script <https://gist.github.com/tomek/6345568>`_
.. _MSYS: http://xhmikosr.1f0.de/tools/
Setup
-----
You must paste my script to directory above your Perl installation. Script was written for Perl installed in MozillaBuild directory: C:\\mozilla-build\\perl-5.18\\x64 or \\x86, so script must be in perl-5.18 dir, if your Perl installation dir is in another place you must edit PATHs in lines 13 & 16.
MSYS can be extracted to any directory you want, you just need to edit PATH in *SET MSYS=I:\\MSYS\\bin*, fg. *SET MSYS=C:\\MSYS\\bin*
Usage
-----
To use script you must open cmd (Win+R, type cmd), navigate to directory where you pasted cpan.bat. Type *cpan.bat x86* if you're using 32bit Hexchat & Perl or *cpan.bat x64* for 64bit version. After that you can use all cpan commands.
Old depreciated method
----------------------
Software
--------
To start building Perl modules you need to download and install this software (in their default install paths):
- `MozillaBuild`_ or `direct link`_
- `Visual Studio 2012 Express for Windows Desktop`_ + `Visual Studio 2012 Update 3`_
- Perl `x86`_ or `x64`_ (Perl **MUST** be installed to C:\\Perl)
.. _MozillaBuild: https://wiki.mozilla.org/MozillaBuild
.. _direct link: http://ftp.mozilla.org/pub/mozilla.org/mozilla/libraries/win32/MozillaBuildSetup-Latest.exe
.. _Visual Studio 2012 Express for Windows Desktop: http://www.microsoft.com/visualstudio/eng/downloads#d-express-windows-desktop
.. _Visual Studio 2012 Update 3: http://www.microsoft.com/en-us/download/details.aspx?id=39305
.. _x86: http://dl.zoitechat.org/misc/perl/perl-5.18.0-x86.7z
.. _x64: http://dl.zoitechat.org/misc/perl/perl-5.18.0-x64.7z
Downloading and Extracting
--------------------------
Start MozillaBuild console (it's in C:\\mozilla-build) *start-msvc11.bat* (if you're using Hexchat x86) or *start-msvc11-x64.bat* (if you're using Hexchat x64).
Type:
.. code-block:: none
mkdir perl; cd perl
(we will work in *perl* directory).
All Perl modules can be downloaded from `CPAN`_ site.
.. _CPAN: http://search.cpan.org/
Now we can download the module which can be done using *wget http://link* command.
Later we extract it using *tar -zxvf module.tar.gz*.
In this guide we will build Net::Telnet module. So we are downloading it and extracting:
.. code-block:: none
wget http://search.cpan.org/CPAN/authors/id/J/JR/JROGERS/Net-Telnet-3.03.tar.gz
tar -zxvf Net-Telnet-3.03.tar.gz
Type *cd Net-Telnet-3.03* and we can start building.
Note on Perl module dependencies
--------------------------------
This building method isn't perfect so if you're building modue which depends on other module, you need to build it before repeating all steps in this guide.
Building
--------
Fist thing we must do is to configure module and point it to our Perl installation using:
.. code-block:: none
c:/perl/bin/perl.exe Makefile.pl
Note: It really depends if it is Makefile.pl or Build.pl, just check in folder and read README.
You should get something like this:
.. code-block:: none
Checking if your kit is complete...
Looks good
Writing Makefile for Net::Telnet
Writing MYMETA.yml and MYMETA.json
It means that module was properly configured. Now we can move to compilation which can be done using:
.. code-block:: none
nmake
After it we can install module using:
.. code-block:: none
nmake install
Now if you didn't get any error you should have Perl module installed and it can be used with Perl script in Hexchat.
Whole operation should look like this:
.. image:: _static/img/perlmodules_terminal.png
:align: center
+1368
View File
File diff suppressed because it is too large. Load diff
Binary file not shown.
File diff suppressed because it is too large. Load diff
+313
View File
@@ -0,0 +1,313 @@
ZoiteChat D-Bus Interface
=======================
For more help you can see the ZoiteChat `plugin interface
documentation <plugins.html>`_ and see some examples in `Python <https://github.com/zoitechat/zoitechat/blob/master/src/common/dbus/example.py>`_ and `C <https://github.com/zoitechat/zoitechat/blob/master/src/common/dbus/example.c>`_.
.. WARNING:: The dbus interface may change in the future.
You can use the "/org/zoitechat/Remote" object with interface
"org.zoitechat.plugin", but his context can be changed by other clients at
any moment and you may receive signal asked by other clients. So for
more complex usage it's better to get your own remote object. Using
"Connect" method on interface "org.zoitechat.connection"
Available methods on *org.zoitechat.connection* interface:
--------------------------------------------------------
- "Connect"
- Parameters:
- gchar\*: filename
- gchar\*: name
- gchar\*: description
- gchar\*: version
- Returns:
- gchar\*: Your own object's path.
- "Disconnect"
- No parameter, no return value. It frees your remote object.
Available methods on *org.zoitechat.plugin* interface:
----------------------------------------------------
- "Command"
- Parameters:
- gchar\*: the command name without the "/". (e.g. "nick pseudo")
- "Print"
- Parameters:
- gchar\*: text to print on the ZoiteChat window.
- "FindContext"
- Parameters:
- gchar\*: the server name. Can be NULL.
- gchar\*: the channel name. Can be NULL.
- Returns:
- guint: context ID.
- "GetContext"
- Returns:
- guint: current context's ID.
- "SetContext"
- Parameters:
- guint: context ID to switch, returned by "FindContext" or
"GetContext"
- Returns:
- gboolean:
- 1: Success.
- 0: Failure.
- "GetInfo"
- Parameters:
- gchar\*: ID of the information you want.
- Returns:
- gchar\*: information you requested.
- "GetPrefs"
- Parameters:
- gchar\*: Setting name required.
- Returns:
- int:
- 0: Failed.
- 1: Returned a string.
- 2: Returned an integer.
- 3: Returned a boolean.
- gchar\*: the information requested if it's a string.
- int: the information requested if it's a integer or boolean.
- "HookCommand"
- Parameters:
- gchar\*: Name of the command (without the forward slash).
- int: Priority of this command.
- gchar\*: String of text to display when the user executes /help
for this command. May be NULL if you're lazy.
- int: Value to returns when the command is catched. See
ZOITECHAT\_EAT\_\*.
- Returns:
- guint: The ID of the hook.
- "HookServer"
- Parameters:
- gchar\*: Name of the server event.
- int: Priority of this command.
- int: Value to returns when the command is catched. See
ZOITECHAT\_EAT\_\*.
- Returns:
- guint: The ID of the hook.
- "HookPrint"
- Parameters:
- gchar\*: Name of the print event.
- int: Priority of this command.
- int: Value to returns when the command is catched. See
ZOITECHAT\_EAT\_\*.
- Returns:
- guint: The ID of the hook.
- "Unhook"
- Parameters:
- guint: ID of the hook to unhook. (the return value of
"HookCommand", "HookServer" or "HookPrint")
- "ListGet"
- Parameters:
- gchar\*: The list name.
- Returns:
- guint: List ID.
- "ListNext"
- Parameters:
- guint: List ID returned by "ListGet".
- Returns:
- gboolean: says if there is no more item in the list.
- "ListStr"
- Parameters:
- guint: List ID returned by "ListGet".
- gchar\*: Name of the information needed.
- Returns:
- gchar\*: The information requested.
Warning: "context" attribute of "channels" list should be get with
"ListInt"
- "ListInt"
- Parameters:
- guint: List ID returned by "ListGet".
- gchar\*: Name of the information needed.
- Returns:
- guint: The information requested.
- "ListTime"
- Parameters:
- guint: List ID returned by "ListGet".
- gchar\*: Name of the information needed.
- Returns:
- guint64: The information requested.
- "ListFields"
- Parameters:
- gchar\*: The list name.
- Returns:
- gchar\*\*: information names in this list.
- "ListFree"
- Parameters:
- guint: List ID returned by "ListGet".
- "EmitPrint"
- Parameters:
- gchar\*: Text event to print.
- gchar\*\*: NULL terminated array of string.
- Returns:
- gboolean:
- 1: Success.
- 0: Failure.
- "Nickcmp"
- Parameters:
- gchar\*: String to compare.
- gchar\*: String to compare.
- Returns:
- int: An integer less than, equal to, or greater than zero if s1
is found, respectively, to be less than, to match, or be
greater than s2.
- "Strip"
- Parameters:
- gchar\*: String to strip.
- int: Length of the string (or -1 for NULL terminated).
- int: Bit-field of flags:
- 0: Strip mIRC colors.
- 1: Strip text attributes.
- Returns:
- gchar\*: stripped string.
- "SendModes"
- Parameters:
- gchar\*\*: NULL terminated array of targets (strings). The
names of people whom the action will be performed on.
- int: Maximum modes to send per line.
- gchar: Mode sign, '-' or '+'.
- gchar: Mode char, e.g. 'o' for Ops.
Available signals:
------------------
- "ServerSignal"
- Parameters:
- gchar\*\*: word returned by ZoiteChat.
- gchar\*\*: word\_eol returned by ZoiteChat.
- guint: the ID of the hook (the return value of "HookServer").
- guint: the ID of the context where the event come from.
- "CommandSignal"
- Parameters:
- gchar\*\*: word returned by ZoiteChat.
- gchar\*\*: word\_eol returned by ZoiteChat.
- guint: the ID of the hook (the return value of "HookCommand").
- guint: the ID of the context where the event come from.
- "PrintSignal"
- Parameters:
- gchar\*\*: word returned by ZoiteChat.
- guint: the ID of the hook (the return value of "HookPrint").
- guint: the ID of the context where the event come from.
- "UnloadSignal"
- Emitted when the user asks to unload your program. Please exit(0);
when received!
+614
View File
@@ -0,0 +1,614 @@
ZoiteChat Lua Interface
=====================
Scripts
-------
Just like many other language plugins, the Lua plugin provides the
commands ``/load``, ``/unload``, and ``/reload`` which operate on Lua
scripts. You can forcibly load a Lua script with any extension by using
``/lua load``, ``/lua unload`` or ``/lua reload`` instead. All files
ending in ``.lua`` and ``.luac`` in the addons directory will be
automatically loaded on startup.
Every script gets its own isolated state. The state is initialized with
the Lua standard library, and has a global table called ``zoitechat``
providing the API, which is described below.
Commands
--------
.. program:: lua
.. option:: /lua load <filename>
.. option:: /load <filename>
Loads a script with the given filename. ``/load`` will only load files
ending in ``.lua`` and ``.luac``.
.. option:: /lua unload <filename>
.. option:: /unload <filename>
Unloads a script with the given filename.
.. option:: /reload <filename>`
.. option:: /lua reload <filename>`
Reloads a script with the given filename.
.. option:: /lua list
Lists loaded Lua scripts.
.. option:: /lua exec <code>
Executes given code in the interpreter.
.. option:: /lua console
Opens an interactive console. Messages to that tab are intercepted and
interpreted as code.
.. option:: /lua inject <filename> <code>
Executes given code in the context of a given script (which has to be
loaded).
.. option:: /lua reset
Reloads the interpreter (but not the scripts).
Environment
-----------
.. note::
This only *directly* applies to **Windows** since other platforms rely on
the Lua version and libraries installed on your system. It is most
likely still of use, however.
The embedded Lua implementation is `LuaJIT`_ . LuaJIT by itself provides a
few `extensions`_, such as the the ``bit`` and ``ffi`` modules. It
generally follows Lua 5.1 and has a few backwards compatible additions from
Lua 5.2, which are explained on the `extensions`_ page.
.. _LuaJIT: http://luajit.org/luajit.html
.. _extensions: http://luajit.org/extensions.html
Hexchat then provides the global ``zoitechat`` table, which is described in
detail in the :ref:`lua-API` section below.
Additionally, the ``lgi`` module is available, which provides
GObject-introspection, since it is used by Hexchat internally. For looking
up mappings, the `Python GObject Introspection API reference <http://lazka.github.io/pgi-docs/index.html>`_
can be used, as it is very similar.
References
~~~~~~~~~~
LuaJIT
http://luajit.org/luajit.html
LuaJIT Extensions
http://luajit.org/extensions.html
BitOp Library
http://bitop.luajit.org/api.html
FFI Library
http://luajit.org/ext_ffi.html
LGI project page
https://github.com/pavouk/lgi
LGI User Guide
https://github.com/pavouk/lgi/blob/master/docs/guide.md
Gnome API Reference
https://developer.gnome.org/references
Python GObject Introspection API Reference
http://lazka.github.io/pgi-docs/index.html
.. _lua-API:
API
---
The ZoiteChat API is accessible through the ``zoitechat`` table.
General functions
~~~~~~~~~~~~~~~~~
zoitechat.register(name, version, description)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Upon initialization every script should introduce itself by calling
this function. Failure to do so will result in the script being
unloaded immediately.
zoitechat.command(cmd)
^^^^^^^^^^^^^^^^^^^^
Executes the command ``cmd`` in the current context, as if
``/cmd`` was typed by the user.
See ``/help`` for valid commands and their documentation.
zoitechat.print(...)
^^^^^^^^^^^^^^^^^^
Prints zero or more values to the current tab. This function also
replaces the global ``print`` function.
zoitechat.emit_print(event, ...)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Emits a text event (can be found in Settings->Text Events) into the
current tab. ``...`` are the strings that are passed as arguments to the
event. At the moment due to internal limitations, only 5 arguments are
passed as there aren't any text events with more arguments.
zoitechat.send_modes(targets, mode[, max])
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Sets multiple modes in the current context, possibly grouping them
together up to the server limit. ``targets`` needs to be an array of
strings, ``mode`` needs to be a string of 2 characters: ``+`` or ``-``
followed by the mode letter. ``max`` is the number of modes on one line,
if omitted the serverside limit is used.
zoitechat.nickcmp(a, b)
^^^^^^^^^^^^^^^^^^^^^
Compares 2 strings case-insensitively, in accordance with current
server's casemapping. Returns a negative number if ``a`` is less than
``b``, zero if they are equal, and a positive number if ``a`` is more
than ``b``.
zoitechat.strip(string[, keep_colors[, keep_attrs]])
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Removes color codes from the given string. If ``keep_colors`` is a
truthy value, colors are not removed. If ``keep_attrs`` is a truthy
value, attributes such as bold or underline are not removed. Returns the
resulting string.
zoitechat.get_info(id)
^^^^^^^^^^^^^^^^^^^^
Returns information about the current context. ``id`` is a string
determining the information you want. It can be one of the following
(case sensitive):
==================== ====================================================================================================
ID Return value
==================== ====================================================================================================
away Away reason or ``nil`` if you are not away
channel Current context's name
charset Character set used in the current context
configdir ZoiteChat config directory, e.g. ``/home/user/.config/zoitechat``
event_text <name> Text event format string for ``<name>``
host Real hostname of the server you connected to
inputbox The input box contents, what the user has typed
libdirfs Library directory. e.g. ``/usr/lib/zoitechat``. The same directory is used for autoloading plugins
modes Channel modes, or ``nil`` if not known
network Current network name, or ``nil`` if not known
nick Your current nickname
password Password for this network or ``nil``
server Current server name (what the server claims to be) or ``nil`` if you are not connected
topic Current channel topic
version ZoiteChat version number
win_status Window status: ``active``, ``hidden`` or ``normal``
win_ptr A light userdata pointer to the native window. GtkWindow on unix, HWND on windows
gtkwin_ptr A light userdata pointer to a GtkWindow. (Usable within lgi)
==================== ====================================================================================================
zoitechat.iterate(list)
^^^^^^^^^^^^^^^^^^^^^
Iterate through the list ``list``. To be used with generic for-loops in
the following fashion:
.. code-block:: lua
for chan in zoitechat.iterate("channels") do
print(chan.server .. ": " .. chan.channel)
end
List of possible values of ``list``, along with respective keys:
+--------------+--------------------------------------------------------------------+
| ``channels`` | List of channels, queries and their servers |
+--------------+--------------------------------------------------------------------+
| Name | Description |
+==============+====================================================================+
| channel | Tab name |
+--------------+--------------------------------------------------------------------+
| channelkey | Channel's key or ``nil`` |
+--------------+--------------------------------------------------------------------+
| chanmodes | Channel modes, e.g. ``beI,k,l`` (2.12.2+) |
+--------------+--------------------------------------------------------------------+
| chantypes | Channel types, e.g. ``#!&`` |
+--------------+--------------------------------------------------------------------+
| context | Tab's context object |
+--------------+--------------------------------------------------------------------+
| flags | - 2^0 = Connected |
| | - 2^1 = Connecting |
| | - 2^2 = Marked away |
| | - 2^3 = End of MOTD |
| | - 2^4 = Has WHOX |
| | - 2^5 = Has IDMSG |
| | - 2^6 = Hide Join/Parts |
| | - 2^7 = Hide Join/Parts unset |
| | - 2^8 = Beep on Message |
| | - 2^9 = Beep on Message unset |
| | - 2^10 = <unused> |
| | - 2^11 = Logging |
| | - 2^12 = Logging unset |
| | - 2^13 = Scrollback |
| | - 2^14 = Scrollback unset |
| | - 2^15 = Strip colors |
| | - 2^16 = Strip colors unset |
| | - 2^17 = Flash tray |
| | - 2^18 = Flash tray unset |
| | - 2^19 = Flash taskbar |
| | - 2^20 = Flash taskbar unset |
+--------------+--------------------------------------------------------------------+
| id | Unique server ID |
+--------------+--------------------------------------------------------------------+
| lag | Lag in milliseconds |
+--------------+--------------------------------------------------------------------+
| maxmodes | Maximum modes per line |
+--------------+--------------------------------------------------------------------+
| network | Network name |
+--------------+--------------------------------------------------------------------+
| nickprefixes | Nickname prefixes, e.g. ``@+`` |
+--------------+--------------------------------------------------------------------+
| nickmodes | Nickname mode chars, e.g. ``ov`` |
+--------------+--------------------------------------------------------------------+
| queue | Number of bytes in the send-queue |
+--------------+--------------------------------------------------------------------+
| server | Server name to which this channel belongs |
+--------------+--------------------------------------------------------------------+
| type | - 1 = Server |
| | - 2 = Channel |
| | - 3 = Dialog |
| | - 4 = Notice |
| | - 5 = SNotice |
+--------------+--------------------------------------------------------------------+
| users | Number of users in this channel |
+--------------+--------------------------------------------------------------------+
+------------+----------------------------------------------+
| ``users`` | Current contexts users |
+------------+----------------------------------------------+
| Name | Description |
+============+==============================================+
| account | Account name or ``nil`` |
+------------+----------------------------------------------+
| away | Away message or ``nil`` |
+------------+----------------------------------------------+
| host | Host name in form of ``user@host`` or ``nil``|
+------------+----------------------------------------------+
| lasttalk | Time stamp of last time they spoke |
+------------+----------------------------------------------+
| nick | Nick name |
+------------+----------------------------------------------+
| prefix | Prefix character such as ``@`` |
+------------+----------------------------------------------+
| realname | Real name |
+------------+----------------------------------------------+
| selected | If they are selected in the userlist |
+------------+----------------------------------------------+
+------------+----------------------------------------------------------------------+
| ``dcc`` | List of DCC file transfers |
+------------+----------------------------------------------------------------------+
| Name | Description |
+============+======================================================================+
| address32 | Address of the remote user (ipv4 address) |
+------------+----------------------------------------------------------------------+
| cps | Bytes per second (speed) |
+------------+----------------------------------------------------------------------+
| destfile | Destination full pathname |
+------------+----------------------------------------------------------------------+
| file | File name |
+------------+----------------------------------------------------------------------+
| nick | Nickname of person who the file is from/to |
+------------+----------------------------------------------------------------------+
| port | TCP port number |
+------------+----------------------------------------------------------------------+
| pos | Bytes sent/received |
+------------+----------------------------------------------------------------------+
| poshigh | Bytes sent/received, high order 32 bits |
+------------+----------------------------------------------------------------------+
| resume | Point at which this file was resumed (or zero if it was not resumed) |
+------------+----------------------------------------------------------------------+
| resumehigh | Point at which this file was resumed, high order 32 bits |
+------------+----------------------------------------------------------------------+
| size | File size in bytes, low order 32 bits (cast it to unsigned) |
+------------+----------------------------------------------------------------------+
| sizehigh | File size in bytes, high order 32 bits |
+------------+----------------------------------------------------------------------+
| status | - 0 = Queued |
| | - 1 = Active |
| | - 2 = Failed |
| | - 3 = Done |
| | - 4 = Connecting |
| | - 5 = Aborted |
+------------+----------------------------------------------------------------------+
| type | - 0 = Send |
| | - 1 = Recieve |
| | - 1 = ChatRecv |
| | - 1 = ChatSend |
+------------+----------------------------------------------------------------------+
+------------+----------------------------------------------+
| ``ignore`` | Current ignore list |
+------------+----------------------------------------------+
| Name | Description |
+============+==============================================+
| mask | Ignore mask, .e.g. ``\*\!\*\@\*.aol.com`` |
+------------+----------------------------------------------+
| flags | - 2^0 = Private |
| | - 2^1 = Notice |
| | - 2^2 = Channel |
| | - 2^3 = CTCP |
| | - 2^4 = Invite |
| | - 2^5 = Unignore |
| | - 2^6 = NoSave |
| | - 2^7 = DCC |
+------------+----------------------------------------------+
+------------+----------------------------------------------------------------------+
| ``notify`` | List of people on notify |
+------------+----------------------------------------------------------------------+
| Name | Description |
+============+======================================================================+
| networks | Networks to which this nick applies. Comma separated. May be ``nil`` |
+------------+----------------------------------------------------------------------+
| nick | Nickname |
+------------+----------------------------------------------------------------------+
| flags | Bit field of flags. 0=Is online. |
+------------+----------------------------------------------------------------------+
| on | Unix timestamp of when user came online. |
+------------+----------------------------------------------------------------------+
| off | Unix timestamp of when user went offline. |
+------------+----------------------------------------------------------------------+
| seen | Unix timestamp of when user the user was last verified still online. |
+------------+----------------------------------------------------------------------+
zoitechat.props
^^^^^^^^^^^^^
A table containing the values of a ``"channels"`` list for the current
context.
Preferences
~~~~~~~~~~~
zoitechat.prefs
^^^^^^^^^^^^^
You can access ZoiteChat's settings via this pseudo-table, see ``/set``
for a list of keys. Note that you cannot modify the table. Instead,
you should use ``zoitechat.command("set -quiet <key> <value>")``
There are also these special preferences:
- ``id`` - Unique server id
- ``state_cursor`` - Location of cursor in input box
Hooks
~~~~~
Some hooks are executed in a priority order, and hooks executed earlier
can prevent later hooks from being invoked. The following constants
determine priorities of such hooks and are passed to the hooking
function:
- ``zoitechat.PRI_HIGHEST`` - The highest priority.
- ``zoitechat.PRI_HIGH``
- ``zoitechat.PRI_NORM`` - The default priority.
- ``zoitechat.PRI_LOW``
- ``zoitechat.PRI_LOWEST`` - The lowest priority.
The following constants determine whether to pass the event on after the
hook has finished. One of these has to be returned from the callback:
- ``zoitechat.EAT_NONE`` - Let other hooks see the event.
- ``zoitechat.EAT_ZOITECHAT`` - Let other hooks see the event, but prevent
ZoiteChat itself from seeing it.
- ``zoitechat.EAT_PLUGIN`` - Don't let remaining hooks see the event, but
let ZoiteChat know about it.
- ``zoitechat.EAT_ALL`` - Consume this event completely, don't let anyone
else know about it.
All hooking functions return an object which can be later used to remove
the hook, but the hooks are also removed automatically when the script
is unloaded or reloaded.
Unlike the C and Python APIs, there isn't a userdata value passed to the
hooks. Instead you should use upvalues, closures, and/or anonymous
functions.
zoitechat.hook_command(command, callback[, help[, priority]])
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Hooks the function ``callback`` to be executed whenever ``/command`` is
entered. ``help`` is the helptext for the ``/help`` command. Returns a
hook object. The callback receives an array of words, and an array of
word\_eols as arguments.
If ``command`` is nil, then all non-command text is hooked instead,
including ``/say``.
zoitechat.hook_print(event, callback[, priority])
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Hooks the function ``callback`` to be executed whenever the text event
``event`` is to be printed. Returns a hook object. The callback receives
the array of event's arguments as its only argument.
There are also a few extra events you can hook using this function:
* ``Open Context`` - Emitted when a new context is created.
* ``Close Context`` - Emitted when a context is closed.
* ``Focus Tab`` - Emitted when a tab is brought to the front.
* ``Focus Window`` - Emitted when a toplevel window is focused, or the main tab-window is focused by the window manager.
* ``DCC Chat Text`` - Emitted when some text from a DCC Chat arrives. It provides these elements in the word list:
* Address
* Port
* Nick
* Message
* ``Key Press`` - Emitted when some keys are pressed in the input box. It provides these elements in the word list:
* Key Value
* Modifier bitfield (Shift, CapsLock, Alt, etc)
* String version of the key
* Length of the string (may be 0 for unprintable keys)
zoitechat.hook_server(command, callback[, priority])
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Hooks the function ``callback`` to be executed whenever ``command`` is
received from the server. Returns a hook object. The callback receives
an array of words, and an array of word\_eols as arguments.
If ``command`` is nil, then the callback is called for every received
line.
zoitechat.hook_timer(interval, callback)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Hooks the function ``callback`` to be executed after ``inverval``
milliseconds. Returns a hook object. As long as the callback returns a
truthy value, it is scheduled to happen after the same preiod of time.
zoitechat.hook_unload(callback)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Hooks the function ``callback`` to be executed when the current script
is unloaded. Returns a hook object.
hook:unhook() and zoitechat.unhook(hook)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Removes the given hook. A hook can only be removed once.
Contexts
~~~~~~~~
A context corresponds to a ZoiteChat window or tab. Some of the functions
in ``zoitechat.*`` will do something in the current tab. Using contexts
you can perform such actions in other tabs instead. Two context objects
can be tested for equality using the ``==`` operator, which will return
true if the contexts refer to the same tab. All methods of a context
object will error if the object is invalidated and points to a tab that
is closed.
zoitechat.get_context()
^^^^^^^^^^^^^^^^^^^^^
Returns a context object for the current context.
zoitechat.find_context(server_name, channel_name)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Finds a context object for a tab on the given channel of the given
channel. If ``server_name`` is nil, it searches for the given channel or
query across all servers. If ``channel_name`` is nil, finds the
frontmost tab of the given server. If both are ``nil``, returns current
context. In any case, if the specified tab was not found, the function
returns nil.
ctx:set() and zoitechat.set_context(ctx)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Makes ``ctx`` the "current" context. All ``zoitechat.*`` functions will be using
this context. This setting only persists within one event. Next time any
of the callbacks is called, the current context will be set to the
actual one.
Returns a boolean indicating whether the context was successfully set. Failure
will only occur if the context has been closed.
ctx:find_context(server_name, channel_name)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Identical to ``zoitechat.find_context``, except the defaults are based on
the current context.
ctx:print(...)
^^^^^^^^^^^^^^
Prints zero or more values in the given context.
ctx:emit_print(event, ...)
^^^^^^^^^^^^^^^^^^^^^^^^^^
Emits a text event into the given context. See ``zoitechat.emit_print``.
Returns a boolean indicating success.
ctx:command(cmd)
^^^^^^^^^^^^^^^^
Executes the command ``/cmd`` in the given context. See
``zoitechat.command``.
ctx:nickcmp(a, b)
^^^^^^^^^^^^^^^^^
Compares 2 strings using casemapping from the given context. See
``zoitechat.nickcmp``.
ctx:get_info(id)
^^^^^^^^^^^^^^^^
Returns information about the given context. See ``zoitechat.get_info``.
ctx:iterate(list)
^^^^^^^^^^^^^^^^^
Iterate through a list within the given context. See
``zoitechat.iterate``.
Plugin preferences
~~~~~~~~~~~~~~~~~~
zoitechat.pluginprefs
^^^^^^^^^^^^^^^^^^^
To persistently store your script's settings, you can use this pseudo-table.
The values inside will persist across script reloads, ZoiteChat restarts, and
reboots. Currently, you can only store and read strings and numbers associated
to string keys, and iterate through the table with ``pairs()``.
Attributes
~~~~~~~~~~
Attributes correspond to extra metadata for messages, such as
server-time (currently the only supported attribute). Some functions
have attributes-enhanced versions.
zoitechat.attrs()
^^^^^^^^^^^^^^^
Returns a new attributes object. It has only one field:
``server_time_utc``.
zoitechat.emit_print_attrs(attrs, event, ...) and ctx:emit_print_attrs(attrs, event, ...)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Analogous to ``zoitechat.emit_print`` and ``ctx:emit_print`` respectively,
but passes an extra attributes argument.
zoitechat.hook_print_attrs(event, callback[, priority])
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Identical to ``zoitechat.hook_print``, except that the callback receives
an additional second argument with an attributes object and that the
aforementioned extra events cannot be hooked.
zoitechat.hook_server_attrs(command, callback[, priority])
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Identical to ``zoitechat.hook_server``, except that the callback receives
an additional third argument with an attributes object.
+890
View File
@@ -0,0 +1,890 @@
ZoiteChat Perl Interface
======================
Introduction
------------
This is the Perl interface for ZoiteChat.
Constants
---------
Priorities
~~~~~~~~~~
- **ZoiteChat::PRI_HIGHEST**
- **ZoiteChat::PRI_HIGH**
- **ZoiteChat::PRI_NORM**
- **ZoiteChat::PRI_LOW**
- **ZoiteChat::PRI_LOWEST**
Return values
~~~~~~~~~~~~~
- **ZoiteChat::EAT_NONE** - pass the event along
- **ZoiteChat::EAT_ZOITECHAT** - don't let ZoiteChat see this event
- **ZoiteChat::EAT_PLUGIN** - don't let other scripts and plugins see
this event but ZoiteChat will still see it
- **ZoiteChat::EAT_ALL** - don't let anything else see this event
Timer and fd hooks
^^^^^^^^^^^^^^^^^^
- **ZoiteChat::KEEP** - keep the timer going or hook watching the
handle
- **ZoiteChat::REMOVE** - remove the timer or hook watching the handle
hook\_fd flags
~~~~~~~~~~~~~~
- **ZoiteChat::FD_READ** - invoke the callback when the handle is ready
for reading
- **ZoiteChat::FD_WRITE** - invoke the callback when the handle is
ready for writing
- **ZoiteChat::FD_EXCEPTION** - invoke the callback if an exception
occurs
- **ZoiteChat::FD_NOTSOCKET** - indicate that the handle being hooked
is not a socket
Exports
~~~~~~~
The following tags are supported:
- ``:all`` - exports everything
- ``:constants`` - exports all the constants
- ``:hooks`` - exports ``hook_*`` functions as well us ``unhook``
- ``:util`` - everything else
By default only the constants are exported.
Functions
---------
ZoiteChat::register( $name, $version, [$description,[$callback]] )
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- ``$name`` - The name of this script
- ``$version`` - This script's version
- ``$description`` - A description for this script
- ``$callback`` - This is a function that will be called when the is
script unloaded. This can be either a reference to a function or an
anonymous sub reference.
This is the first thing to call in every script.
ZoiteChat::hook_server( $message, $callback, [\%options] )
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ZoiteChat::hook_command( $command, $callback, [\%options] )
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ZoiteChat::hook_print( $event,$callback, [\%options] )
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ZoiteChat::hook_timer( $timeout,$callback, [\%options | $data] )
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ZoiteChat::hook_fd( $handle, $callback, [ \%options ] )
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
These functions can be to intercept various events. ``hook_server`` can be
used to intercept any incoming message from the IRC server.
``hook_command`` can be used to intercept any command, if the command
doesn't currently exist then a new one is created. ``hook_print`` can be
used to intercept any of the events listed in *Setttings* ``->`` *Text
Events*. ``hook_timer`` can be used to create a new timer
- **$message** - server message to hook such as PRIVMSG
- **$command** - command to intercept, without the leading /
- **$event** - one of the events listed in *Settings* ``->`` *Text
Events*
- **$timeout** - timeout in milliseconds
- **$handle** - the I/O handle you want to monitor with hook\_fd.
This must be something that has a fileno. See perldoc -f fileno or
`fileno <http://perldoc.perl.org/functions/fileno.html>`_
- **$callback** - callback function, this is called whenever the
hooked event is trigged, the following are the conditions that will
trigger the different hooks. This can be either a reference to a
function or an anonymous sub reference.
- **\%options** - a hash reference containing addional options for
the hooks
Valid keys for %options:
+------------+--------------------------------------------------------------------------+
| data | Additional data that is to be associated with the hook. For timer hooks |
| | this value can be provided either as ``ZoiteChat::hook_timer( $timeout, |
| | cb,{data=> data})`` or ``ZoiteChat::hook_timer( $timeout, $cb, $data )``. |
| | However, this means that hook\_timer cannot be provided with a hash |
| | reference containing data as a key. |
| | |
| | Example: |
| | |
| | .. code-block:: none |
| | |
| | my $options = { data => [@arrayOfStuff] }; |
| | ZoiteChat::hook_timer( $timeout, $cb, $options ); |
| | |
| | In this example, the timer's data will be ``[@arrayOfStuff]`` and not |
| | ``{ data => [@arrayOfStuff] }``. This key is valid for all of the hook |
| | functions. Default is ``undef``. |
+------------+--------------------------------------------------------------------------+
| priority | Sets the priority for the hook. It can be set to one of the |
| | ``ZoiteChat::PRI_*`` constants. This key only applies to server, command |
| | and print hooks. Default is ``ZoiteChat::PRI_NORM``. |
+------------+--------------------------------------------------------------------------+
| help\_text | Text displayed for /help $command. This key only applies to command |
| | hooks. Default is "". |
+------------+--------------------------------------------------------------------------+
| flags | Specify the flags for a fd hook. See hook fd flags section for valid |
| | values. On Windows if the handle is a pipe you specify |
| | ``ZoiteChat::FD_NOTSOCKET`` in addition to any other flags you might be |
| | using. This key only applies to fd hooks. Default is |
| | ``ZoiteChat::FD_READ``. |
+------------+--------------------------------------------------------------------------+
When callbacks are invoked
^^^^^^^^^^^^^^^^^^^^^^^^^^
Each of the hooks will be triggered at different times depending on the
type of hook.
+---------------+------------------------------------------------------------------------------------+
| Hook Type | When the callback will be invoked |
+===============+====================================================================================+
| server hooks | A ``$message`` message is received from the server. |
+---------------+------------------------------------------------------------------------------------+
| command hooks | The ``$command`` command is executed, either by the user or from a script. |
+---------------+------------------------------------------------------------------------------------+
| print hooks | X-Chat is about to print the message for the ``$event`` event. |
+---------------+------------------------------------------------------------------------------------+
| timer hooks | Called every ``$timeout`` milliseconds (1000 milliseconds are 1 second). |
| | The callback will be executed in the same context where the ``hook_timer`` was |
| | called, or if the context no longer exists then it will execute in a random |
| | context. |
+---------------+------------------------------------------------------------------------------------+
| fd hooks | Depends on the flags that were passed to ``hook_fd``. See ``hook_fd`` flags in the |
| | section above. |
+---------------+------------------------------------------------------------------------------------+
The value return from these hook functions can be passed to
``ZoiteChat::unhook`` to remove the hook.
Callback Arguments
^^^^^^^^^^^^^^^^^^
All callback functions will receive their arguments in ``@_`` like every
other Perl subroutine.
- Server and command callbacks
``$_[0]`` - array reference containing the IRC message or command and
arguments broken into words example:
/command arg1 arg2 arg3
- ``$_[0][0]`` - command
- ``$_[0][1]`` - arg1
- ``$_[0][2]`` - arg2
- ``$_[0][3]`` - arg3
``$_[1]`` - array reference containing the Nth word to the last word
example:
/command arg1 arg2 arg3
- ``$_[1][0]`` - command arg1 arg2 arg3
- ``$_[1][1]`` - arg1 arg2 arg3
- ``$_[1][2]`` - arg2 arg3
- ``$_[1][3]`` - arg3
``$_[2]`` - the data that was passed to the hook function
- Print callbacks
``$_[0]`` - array reference containing the values for the text event,
see *Settings* ``->`` *Text Events*
``$_[1]`` - the data that was passed to the hook function
- Timer callbacks
``$_[0]`` - the data that was passed to the hook function
- fd callbacks
``$_[0]`` - the handle that was passed to hook\_fd ``$_[1]`` - flags
indicating why the callback was called ``$_[2]`` - the data that was
passed to the hook function
Callback return values
^^^^^^^^^^^^^^^^^^^^^^
All server, command and print callbacks should return one of the
``ZoiteChat::EAT_*`` constants.
Timer callbacks can return ``ZoiteChat::REMOVE`` to remove the timer or
``ZoiteChat::KEEP`` to keep it going.
Miscellaneous Hook Related Information
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
For server hooks, if ``$message`` is "RAW LINE" then ``$cb`` will be
called for every IRC message that ZoiteChat receives.
For command hooks if ``$command`` is "" then ``$cb`` will be called for
messages entered by the user that is not a command.
For print hooks besides those events listed in *Settings* ``->`` *Text
Events*, these additional events can be used.
+-----------------+--------------------------------------------------------------------------------------------+
| Event | Description |
+=================+============================================================================================+
| "Open Context" | a new context is created |
+-----------------+--------------------------------------------------------------------------------------------+
| "Close Context" | a context has been closed |
+-----------------+--------------------------------------------------------------------------------------------+
| "Focus Tab" | when a tab is brought to the front |
+-----------------+--------------------------------------------------------------------------------------------+
| "Focus Window" | when a top level window is focused or the main tab window is focused by the window manager |
+-----------------+--------------------------------------------------------------------------------------------+
| "DCC Chat Text" | when text from a DCC Chat arrives. $_[0] will have these values |
| | |
| | - ``$_[0][0]`` - Address |
| | - ``$_[0][1]`` - Port |
| | - ``$_[0][2]`` - Nick |
| | - ``$_[0][3]`` - Message |
+-----------------+--------------------------------------------------------------------------------------------+
| "Key Press" | used for intercepting key presses |
| | |
| | - ``$_[0][0]`` - key value |
| | - ``$_[0][1]`` - state bitfield, 1 - shift, 4 - control, 8 - alt |
| | - ``$_[0][2]`` - string version of the key which might be empty for unprintable keys |
| | - ``$_[0][3]`` - length of the string in ``$_[0][2]`` |
+-----------------+--------------------------------------------------------------------------------------------+
ZoiteChat::unhook( $hook )
~~~~~~~~~~~~~~~~~~~~~~~~
- **$hook** - the hook that was previously returned by one of the
``ZoiteChat::hook_*`` functions
This function is used to removed a hook previously added with one of the
``ZoiteChat::hook_*`` functions.
It returns the data that was passed to the ZoiteChat::hook_* function
when the hook was added.
ZoiteChat::print( $text | \@lines, [$channel,[$server]] )
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- **$text** - the text to print
- **\@lines** - array reference containing lines of text to be
printed all the elements will be joined together before printing
- **$channel** - channel or tab with the given name where ``$text``
will be printed
- **$server** - specifies that the text will be printed in a channel
or tab that is associated with ``$server``
The first argument can either be a string or an array reference of
strings. Either or both of ``$channel`` and ``$server`` can be ``undef``.
If called as ZoiteChat::print( $text ), it will always return true. If
called with either the channel or the channel and the server specified
then it will return true if a context is found and false otherwise. The
text will not be printed if the context is not found. The meaning of
setting ``$channel`` or ``$server`` to ``undef`` is the same as
find\_context.
ZoiteChat::printf( $format, LIST )
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- **$format** - a format string, see "perldoc -f
`sprintf <http://perldoc.perl.org/functions/sprintf.html>`_" for
further details
- **LIST** - list of values for the format fields
ZoiteChat::command( $command | \@commands, [$channel,[$server]] )
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- **$command** - the command to execute, without the leading /
- **\@commands** - array reference containing a list of commands to
execute
- **$channel** - channel or tab with the given name where
``$command`` will be executed
- **$server** - specifies that the command will be executed in a
channel or tab that is associated with ``$server``
The first argument can either be a string or an array reference of
strings. Either or both of ``$channel`` and ``$server`` can be ``undef``.
If called as ZoiteChat::command( $command ), it will always return true.
If called with either the channel or the channel and the server
specified then it will return true if a context is found and false
otherwise. The command will not be executed if the context is not found.
The meaning of setting ``$channel`` or ``$server`` to ``undef`` is the same
as find\_context.
ZoiteChat::commandf( $format, LIST )
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- **$format** - a format string, see "perldoc -f
`sprintf <http://perldoc.perl.org/functions/sprintf.html>`_" for
further details
- **LIST** - list of values for the format fields
ZoiteChat::find_context( [$channel, [$server]] )
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- **$channel** - name of a channel
- **$server** - name of a server
Either or both of ``$channel`` and ``$server`` can be ``undef``. Calling
``ZoiteChat::find_context()`` is the same as calling
``ZoiteChat::find_context( undef, undef)`` and
``ZoiteChat::find_context( $channel )`` is the same as
``ZoiteChat::find_context( $channel, undef )``.
If ``$server`` is ``undef``, find any channel named ``$channel``. If
``$channel`` is ``undef``, find the front most window or tab named
``$server``.If both ``$channel`` and ``$server`` are ``undef``, find the
currently focused tab or window.
Return the context found for one of the above situations or ``undef`` if
such a context cannot be found.
ZoiteChat::get_context()
~~~~~~~~~~~~~~~~~~~~~~
Returns the current context.
ZoiteChat::set_context( $context | $channel,[$server] )
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- **$context** - context value as returned from ``get_context``,
``find_context`` or one of the fields in the list of hashrefs
returned by ``list_get``
- **$channel** - name of a channel you want to switch context to
- **$server** - name of a server you want to switch context to
See ``find_context`` for more details on ``$channel`` and ``$server``.
Returns true on success, false on failure.
ZoiteChat::get_info( $id )
~~~~~~~~~~~~~~~~~~~~~~~~
- **$id** - one of the following case sensitive values
+-------------------------+---------------------------------------------------------------------------------------------------------------------+-----------------------+
| ID | Return value | Associated Command(s) |
+=========================+=====================================================================================================================+=======================+
| away | away reason or ``undef`` if you are not away | AWAY, BACK |
+-------------------------+---------------------------------------------------------------------------------------------------------------------+-----------------------+
| channel | the original name of the tab, not affected by SETTAB | SETTAB |
+-------------------------+---------------------------------------------------------------------------------------------------------------------+-----------------------+
| charset | character-set used in the current context | CHARSET |
+-------------------------+---------------------------------------------------------------------------------------------------------------------+-----------------------+
| configdir | ZoiteChat config directory encoded in UTF-8. Examples: | |
| | /home/user/.config/zoitechat | |
| | C:\Users\user\Appdata\Roaming\ZoiteChat | |
+-------------------------+---------------------------------------------------------------------------------------------------------------------+-----------------------+
| event_text <Event Name> | text event format string for <Event name> | |
| | Example: | |
| | | |
| | ``my $channel_msg_format = ZoiteChat::get_info( "event_text Channel Message" );`` | |
+-------------------------+---------------------------------------------------------------------------------------------------------------------+-----------------------+
| host | real hostname of the current server | |
+-------------------------+---------------------------------------------------------------------------------------------------------------------+-----------------------+
| id | connection id | |
+-------------------------+---------------------------------------------------------------------------------------------------------------------+-----------------------+
| inputbox | contents of the inputbox | SETTEXT |
+-------------------------+---------------------------------------------------------------------------------------------------------------------+-----------------------+
| libdirfs | the system wide directory where ZoiteChat will look for plugins. this string is in the same encoding | |
| | as the local file system | |
+-------------------------+---------------------------------------------------------------------------------------------------------------------+-----------------------+
| modes | the current channels modes or ``undef`` if not known | MODE |
+-------------------------+---------------------------------------------------------------------------------------------------------------------+-----------------------+
| network | current network name or ``undef``, this value is taken from the Network List | |
+-------------------------+---------------------------------------------------------------------------------------------------------------------+-----------------------+
| nick | current nick | NICK |
+-------------------------+---------------------------------------------------------------------------------------------------------------------+-----------------------+
| nickserv | nickserv password for this network or ``undef``, this value is taken from the Network List | |
+-------------------------+---------------------------------------------------------------------------------------------------------------------+-----------------------+
| server | current server name | |
| | (what the server claims to be) ``undef`` if not connected | |
+-------------------------+---------------------------------------------------------------------------------------------------------------------+-----------------------+
| state\_cursor | current inputbox cursor position in characters | SETCURSOR |
+-------------------------+---------------------------------------------------------------------------------------------------------------------+-----------------------+
| topic | current channel topic | TOPIC |
+-------------------------+---------------------------------------------------------------------------------------------------------------------+-----------------------+
| version | ZoiteChat's version number | |
+-------------------------+---------------------------------------------------------------------------------------------------------------------+-----------------------+
| win_status | status of the ZoiteChat window, possible values are "active", "hidden" and "normal" | GUI |
+-------------------------+---------------------------------------------------------------------------------------------------------------------+-----------------------+
| win\_ptr | native window pointer, GtkWindow * on Unix, HWND on Win32. | |
| | On Unix if you have the Glib module installed you can use my ``$window = | |
| | Glib::Object->new\_from\_pointer( ZoiteChat::get_info( "win\_ptr" ) );`` to get a Gtk2::Window object. | |
| | Additionally when you have detached tabs, each of the windows will return a different win\_ptr | |
| | for the different ``Gtk2::Window`` objects. | |
| | See `char\_count.pl <http://xchat.cvs.sourceforge.net/viewvc/xchat/xchat2/plugins/perl/char_count.pl?view=markup>`_ | |
| | for a longer example of a script that uses this to show how many characters you currently have in your input box. | |
+-------------------------+---------------------------------------------------------------------------------------------------------------------+-----------------------+
| gtkwin_ptr | similar to win_ptr except it will always be a GtkWindow * | |
+-------------------------+---------------------------------------------------------------------------------------------------------------------+-----------------------+
This function is used to retrieve certain information about the current
context. If there is an associated command then that command can be used
to change the value for a particular ID.
ZoiteChat::get_prefs( $name )
~~~~~~~~~~~~~~~~~~~~~~~~~~~
- **$name** - name of a ZoiteChat setting (available through the /set
command)
This function provides a way to retrieve ZoiteChat's setting information.
Returns ``undef`` if there is no setting called called ``$name``.
ZoiteChat::emit_print( $event, LIST )
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- **$event** - name from the Event column in *Settings* ``->`` *Text
Events*
- **LIST** - this depends on the Description column on the bottom of
*Settings* ``->`` *Text Events*
This functions is used to generate one of the events listed under
*Settings* ``->`` *Text Events*.
Note: when using this function you **must** return ``ZoiteChat::EAT_ALL``
otherwise you will end up with duplicate events. One is the original and
the second is the one you emit.
Returns true on success, false on failure.
ZoiteChat::send_modes( $target | \@targets, $sign, $mode, [ $modes_per_line ] )
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- **$target** - a single nick to set the mode on
- **\@targets** - an array reference of the nicks to set the mode
on
- **$sign** - the mode sign, either '+' or '-'
- **$mode** - the mode character such as 'o' and 'v', this can only
be one character long
- **$modes_per_line** - an optional argument maximum number of modes
to send per at once, pass 0 use the current server's maximum
(default)
Send multiple mode changes for the current channel. It may send multiple
MODE lines if the request doesn't fit on one.
Example:
.. code-block:: perl
use strict;
use warnings;
use ZoiteChat qw(:all);
hook_command( "MODES", sub {
my (undef, $who, $sign, $mode) = @{$_[0]};
my @targets = split /,/, $who;
if( @targets > 1 ) {
send_modes( \@targets, $sign, $mode, 1 );
} else {
send_modes( $who, $sign, $mode );
}
return EAT_ZOITECHAT;
});
ZoiteChat::nickcmp( $nick1, $nick2 )
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- **$nick1, $nick2** - the two nicks or channel names that are to be
compared
The comparsion is based on the current server. Either an
`RFC1459 <http://www.ietf.org/rfc/rfc1459.txt>`_ compliant string
compare or plain ascii will be using depending on the server. The
comparison is case insensitive.
Returns a number less than, equal to or greater than zero if ``$nick1``
is found respectively, to be less than, to match, or be greater than
``$nick2``.
ZoiteChat::get_list( $name )
~~~~~~~~~~~~~~~~~~~~~~~~~~
- **$name** - name of the list, one of the following: "channels",
"dcc", "ignore", "notify", "users"
This function will return a list of hash references. The hash references
will have different keys depend on the list. An empty list is returned
if there is no such list.
"channels" - list of channels, querys and their server
+--------------+-----------------------------------------------------------------------------+
| Key | Description |
+==============+=============================================================================+
| channel | tab name |
+--------------+-----------------------------------------------------------------------------+
| channelkey | the key used to get into the channel |
+--------------+-----------------------------------------------------------------------------+
| chantypes | channel types supported by the server, typically "#&" |
+--------------+-----------------------------------------------------------------------------+
| context | can be used with set_context |
+--------------+-----------------------------------------------------------------------------+
| flags | Server Bits: |
| | |
| | - 0 Connected |
| | - 1 Connecting |
| | - 2 Away |
| | - 3 EndOfMotd(Login complete) |
| | - 4 Has WHOX |
| | - 5 Has IDMSG (FreeNode) |
| | |
| | The following correspond to the /chanopt command |
| | |
| | - 6 Hide Join/Part Message (text_hidejoinpart) |
| | - 7 unused (was for color paste) |
| | - 8 Beep on message (alert_beep) |
| | - 9 Blink Tray (alert_tray) |
| | - 10 Blink Task Bar (alert_taskbar) |
| | |
| | Example of checking if the current context has Hide Join/Part messages set: |
| | |
| | - 1 |
| | - 2 |
| | - 3 |
| | |
| | .. code-block:: perl |
| | |
| | if( ZoiteChat::context_info->{flags} & (1 << 6) ) { |
| | ZoiteChat::print( "Hide Join/Part messages is enabled" ); |
| | } |
+--------------+-----------------------------------------------------------------------------+
| id | Unique server ID |
+--------------+-----------------------------------------------------------------------------+
| lag | lag in milliseconds |
+--------------+-----------------------------------------------------------------------------+
| maxmodes | Maximum modes per line |
+--------------+-----------------------------------------------------------------------------+
| network | network name to which this channel belongs |
+--------------+-----------------------------------------------------------------------------+
| nickmodes | Nickname mode chars e.g. "vo" |
+--------------+-----------------------------------------------------------------------------+
| nickprefixes | Nickname prefixes e.g. "+@" |
+--------------+-----------------------------------------------------------------------------+
| queue | number of bytes in the send queue |
+--------------+-----------------------------------------------------------------------------+
| server | server name to which this channel belongs |
+--------------+-----------------------------------------------------------------------------+
| type | the type of this context |
| | - 1 - server |
| | - 2 - channel |
| | - 3 - dialog |
| | - 4 - notices |
| | - 5 - server notices |
+--------------+-----------------------------------------------------------------------------+
| users | Number of users in a channel |
+--------------+-----------------------------------------------------------------------------+
"dcc" - list of DCC file transfers
+------------+------------------------------------------------------------------+
| Key | Value |
+============+==================================================================+
| address32 | address of the remote user(ipv4 address) |
+------------+------------------------------------------------------------------+
| cps | bytes per second(speed) |
+------------+------------------------------------------------------------------+
| destfile | destination full pathname |
+------------+------------------------------------------------------------------+
| file | file name |
+------------+------------------------------------------------------------------+
| nick | nick of the person this DCC connection is connected to |
+------------+------------------------------------------------------------------+
| port | TCP port number |
+------------+------------------------------------------------------------------+
| pos | bytes sent/received |
+------------+------------------------------------------------------------------+
| poshigh | bytes sent/received, high order 32 bits |
+------------+------------------------------------------------------------------+
| resume | point at which this file was resumed |
| | (zero if it was not resumed) |
+------------+------------------------------------------------------------------+
| resumehigh | point at which this file was resumed, high order 32 bits |
+------------+------------------------------------------------------------------+
| size | file size in bytes low order 32 bits |
+------------+------------------------------------------------------------------+
| sizehigh | file size in bytes, high order 32 bits (when the files is > 4GB) |
+------------+------------------------------------------------------------------+
| status | DCC Status: |
| | |
| | - 0 - queued |
| | |
| | - 2 - failed |
| | - 3 - done |
| | - 4 - connecting |
| | - 5 - aborted |
+------------+------------------------------------------------------------------+
| type | DCC Type: |
| | |
| | - 0 - send |
| | - 1 - receive |
| | - 2 - chatrecv |
| | - 3 - chatsend |
+------------+------------------------------------------------------------------+
"ignore" - current ignore list
+-------+-----------------------------------+
| Key | Value |
+=======+===================================+
| mask | ignore mask. e.g: \*!*\@*.aol.com |
+-------+-----------------------------------+
| flags | Bit field of flags. |
| | |
| | - 0 - private |
| | - 1 - notice |
| | - 2 - channel |
| | - 3 - ctcp |
| | - 4 - invite |
| | - 5 - unignore |
| | - 6 - nosave |
| | - 7 - dcc |
+-------+-----------------------------------+
"notify" - list of people on notify
+----------+----------------------------------------------------------------------------------------------+
| Key | Value |
+==========+==============================================================================================+
| networks | comma separated list of networks where you will be notfified about this user's |
| | online/offline status or ``undef`` if you will be notificed on every network you are |
| | connected to |
+----------+----------------------------------------------------------------------------------------------+
| nick | nickname |
+----------+----------------------------------------------------------------------------------------------+
| flags | 0 = is online |
+----------+----------------------------------------------------------------------------------------------+
| on | time when user came online |
+----------+----------------------------------------------------------------------------------------------+
| off | time when user went offline |
+----------+----------------------------------------------------------------------------------------------+
| seen | time when user was last verified still online |
+----------+----------------------------------------------------------------------------------------------+
The values indexed by on, off and seen can be passed to localtime and
gmtime, see perldoc -f
`localtime <http://perldoc.perl.org/functions/localtime.html>`_ and
perldoc -f `gmtime <http://perldoc.perl.org/functions/gmtime.html>`_ for
more details.
"users" - list of users in the current channel
+----------+-----------------------------------------------------------------------------------------------------------------------------------+
| Key | Value |
+==========+===================================================================================================================================+
| account | account name or ``undef`` (2.9.6+) |
+----------+-----------------------------------------------------------------------------------------------------------------------------------+
| away | away status (boolean) |
+----------+-----------------------------------------------------------------------------------------------------------------------------------+
| host | host name in the form: user@host or ``undef`` if not known |
+----------+-----------------------------------------------------------------------------------------------------------------------------------+
| lasttalk | last time a user was seen talking, this is the an epoch time |
+----------+-----------------------------------------------------------------------------------------------------------------------------------+
| nick | nick name |
+----------+-----------------------------------------------------------------------------------------------------------------------------------+
| prefix | prefix character, .e.g: @ or + |
+----------+-----------------------------------------------------------------------------------------------------------------------------------+
| realname | Real name or ``undef`` |
+----------+-----------------------------------------------------------------------------------------------------------------------------------+
| selected | selected status in the user list, only works when retrieving the user list of the focused tab. |
| | You can use the /USELECT command to select the nicks |
+----------+-----------------------------------------------------------------------------------------------------------------------------------+
"networks" - list of networks and the associated settings from network list
+-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Key | Value |
+===================+=================================================================================================================================================================================+
| autojoins | An object with the following methods: |
| | |
| | - ``channels()`` - returns a list of this networks' autojoin channels in list context, a count of the number autojoin channels in scalar context |
| | - ``keys()`` - returns a list of the keys to go with the channels, the order is the same as the channels, if a channel doesn't have a key, '' will be returned in its |
| | place |
| | - ``pairs()`` - a combination of channels() and keys(), returns a list of (channels, keys) pairs. This can be assigned to a hash for a mapping from channel to key. |
| | - ``as_hash()`` - return the pairs as a hash reference |
| | - ``as_string()`` - the original string that was used to construct this autojoin object, this can be used with the JOIN command to join all the channels in the autojoin list |
| | - ``as_array()`` - return an array reference of hash references consisting of the keys "channel" and "key" |
| | - ``as_bool()`` - returns true if the network has autojoins and false otherwise |
| | |
+-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| connect_commands | An array reference containing the connect commands for a network. An empty array if there aren't any |
+-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| encoding | the encoding for the network |
+-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| flags | a hash reference corresponding to the checkboxes in the network edit window |
| | |
| | |
| | - allow_invalid - true if "Accept invalid SSL certificate" is checked |
| | - autoconnect - true if "Auto connect to this network at startup" is checked |
| | - cycle - true if "Connect to selected server only" is NOT checked |
| | - use_global - true if "Use global user information" is checked |
| | - use_proxy - true if "Bypass proxy server" is NOT checked |
| | - use_ssl - true if "Use SSL for all the servers on this network" is checked |
| | |
+-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| irc_nick1 | Corresponds with the "Nick name" field in the network edit window |
+-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| irc_nick2 | Corresponds with the "Second choice" field in the network edit window |
+-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| irc_real_name | Corresponds with the "Real name" field in the network edit window |
+-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| irc_user_name | Corresponds with the "User name" field in the network edit window |
+-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| network | Name of the network |
+-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| nickserv_password | Corresponds with the "Nickserv password" field in the network edit window |
+-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| selected | Index into the list of servers in the "servers" key, this is used if the "cycle" flag is false |
+-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| server_password | Corresponds with the "Server password" field in the network edit window |
+-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| servers | An array reference of hash references with a "host" and "port" key. If a port is not specified then 6667 will be used. |
+-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
ZoiteChat::user_info( [$nick] )
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- **$nick** - the nick to look for, if this is not given your own
nick will be used as default
This function is mainly intended to be used as a shortcut for when you
need to retrieve some information about only one user in a channel.
Otherwise it is better to use ``get_list``. If ``$nick`` is found a hash
reference containing the same keys as those in the "users" list of
``get_list`` is returned otherwise ``undef`` is returned. Since it relies on
``get_list`` this function can only be used in a channel context.
ZoiteChat::context_info( [$context] )
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- **$context** - context returned from ``get_context``,
``find_context`` and ``get_list``, this is the context that you want
infomation about. If this is omitted, it will default to current
context.
This function will return the information normally retrieved with
``get_info``, except this is for the context that is passed in. The
information will be returned in a hashref. The keys of the hash
are the ``$id`` you would normally supply to ``get_info`` as well as all
the keys that are valid for the items in the "channels" list from
``get_list``. Use of this function is more efficient than calling
``get_list( "channels" )`` and searching through the result.
Example:
.. code-block:: perl
use strict;
use warnings;
use ZoiteChat qw(:all); # imports all the functions documented on this page
register( "User Count", "0.1",
"Print out the number of users on the current channel" );
hook_command( "UCOUNT", \&display_count );
sub display_count {
prnt "There are " . context_info()->{users} . " users in this channel.";
return EAT_ZOITECHAT;
}
ZoiteChat::plugin_pref_set( $setting, $value )
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- **$setting** - name of the setting you want to store
- **$value** - value of that setting
This function allows you to store settings in addon_perl.conf via ZoiteChat.
Returns 1 on success, 0 on failure.
ZoiteChat::plugin_pref_get( $setting )
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- **$setting** - name of the setting you want to retrieve
Returns the value of the specified setting or undef it is not found.
ZoiteChat::plugin_pref_delete( $setting )
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- **$setting** - name of the setting you want to delete
Returns 1 on success (including deletion of not existing settings), 0 on failure.
ZoiteChat::plugin_pref_list( )
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Returns a hashref of all stored settings or an empty hashref on failure.
ZoiteChat::strip_code( $string )
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- **$string** - string to remove codes from
This function will remove bold, color, beep, reset, reverse and
underline codes from ``$string``. It will also remove ANSI escape codes
which might get used by certain terminal based clients. If it is called
in void context ``$string`` will be modified otherwise a modified copy
of ``$string`` is returned.
Examples
--------
Asynchronous DNS resolution with hook\_fd
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: perl
use strict;
use warnings;
use ZoiteChat qw(:all);
use Net::DNS;
hook_command( "BGDNS", sub {
my $host = $_[0][1];
my $resolver = Net::DNS::Resolver->new;
my $sock = $resolver->bgsend( $host );
hook_fd( $sock, sub {
my $ready_sock = $_[0];
my $packet = $resolver->bgread( $ready_sock );
if( $packet->authority && (my @answers = $packet->answer ) ) {
if( @answers ) {
prnt "$host:";
my $padding = " " x (length( $host ) + 2);
for my $answer ( @answers ) {
prnt $padding . $answer->rdatastr . ' ' . $answer->type;
}
}
} else {
prnt "Unable to resolve $host";
}
return REMOVE;
},
{
flags => FD_READ,
});
return EAT_ZOITECHAT;
});
Author Information
-------------------
This manual was originally written by Lian Wan Situ at <atmcmnky [at] yahoo.com>.
+853
View File
@@ -0,0 +1,853 @@
ZoiteChat Python Interface
========================
.. default-domain:: py
Features
--------
Here are some of the features of the python plugin interface:
- Comprehensive, consistent and straightforward API
- Load, unload, reload, and autoload support
- Per plugin independent interpreter state
- Python interactive console
- Python interactive command execution
- Full thread support
- Stdout and stderr redirected to ZoiteChat console
- Dynamic list management
- Nice context treatment
- Plugin preferences
Only Python 3 is supported
~~~~~~~~~~~~~~~~~~~~
HexChat supported both Python 2 and 3, but ZoiteChat only supports Python 3.
If you want to use an old Python 2 HexChat script, you may be able to port it
following the standards below.
Commands
--------
The Python plugin comes with a :command:`py` command that takes these arguments.
.. program:: py
.. option:: load <file>
Load a script with given filename.
:command:`/load` will also work.
.. option:: unload <filename\|module name>
Unload module with given filename, or module name.
:command:`/unload` will also work.
.. option:: reload <filename\|module name>
Reload module with given filename, or module name.
:command:`/reload` will also work.
.. option:: list
List Python scripts loaded
.. option:: exec <command>
Execute given Python command interactively. For example::
/py exec import zoitechat; print(zoitechat.get_info('channel'))
.. option:: console
Open the Python interactive console in a query
``>>python<<``. Every message sent will be intercepted by the Python
plugin interface, and interpreted interactively. Notice that the
console and /py exec commands live in the same interpreter state.
.. option:: about
Show some information about the Python plugin interface.
Autoloading modules
-------------------
If you want some module to be autoloaded together with the Python plugin
interface (which usually loads at startup time), just make sure it has a
``.py`` extension and put it in the :file:`addons` subdir
of ZoiteChat's `config directory <settings.html#config-files>`_.
Context theory
--------------
Before starting to explain what the API offers, I'll do a short
introduction about the ZoiteChat context concept. Not because it's
something hard to understand, but because you'll understand better the
API explanations if you know what I'm talking about.
You can think about a context as an ZoiteChat channel, server, or query
tab. Each of these tabs, has its own context, and is related to a given
server and channel (queries are a special kind of channel).
The *current* context is the one where ZoiteChat passes control to the
module. For example, when ZoiteChat receives a command in a specific
channel, and you have asked ZoiteChat to tell you about this event, the
current context will be set to this channel before your module is
called.
Text Formatting
---------------
- Bold: ``'\002'``
- Color: ``'\003'``
- Hidden: ``'\010'``
- Underline: ``'\037'``
- Original Attributes: ``'\017'``
- Reverse Color: ``'\026'``
- Beep: ``'\007'``
- Italics: ``'\035'`` (2.10.0+)
- Strikethrough: ``'\036'`` (2.16.0+)
For example this will print underlined red text:
.. code-block:: python
print('\037\00304Text!')
Bit fields
----------
Some lists return bit fields which many Python scripters may not be familair with
so here is an example of how to use one:
.. code-block:: python
...
# We know its the 15th bit field we want, this will get that value
text_strip = 1 << 15
text_strip_unset = 1 << 16 # If this is set use the global option
def get_chanopt (channel, option):
for chan in zoitechat.get_list('channels'):
if chan.channel == channel:
return bool(chan.flags & option)
if get_chanopt('#zoitechat', text_strip_unset):
stripped = bool(zoitechat.get_prefs('text_stripcolor_msg'))
else:
stripped = get_chanopt ('#zoitechat', text_strip)
print('Color stripping in #zoitechat is: {}'.format(stripped))
Hello world
-----------
Here is the traditional *hello world* example.
.. code-block:: python
__module_name__ = "helloworld"
__module_version__ = "1.0"
__module_description__ = "Python module example"
print("Hello world!")
This module will print "Hello world!" in the ZoiteChat console, and sleep
forever until it's unloaded. It's a simple module, but already
introduces some concepts. Notice how the module information is set. This
information is obligatory, and will be shown when listing the loaded
ZoiteChat modules.
.. module:: zoitechat
zoitechat module
--------------
The zoitechat module is your passport to every ZoiteChat functionality offered
by the Python plugin interface. Here's a simple example:
.. code-block:: python
import zoitechat
zoitechat.prnt("Hi everyone!")
The following functions are available in the zoitechat module.
Constants and Attributes
~~~~~~~~~~~~~~~~~~~~~~~~
.. data:: PRI_HIGHEST
PRI_HIGH
PRI_NORM
PRI_LOW
PRI_LOWEST
Priority given to hooks.
.. data:: EAT_PLUGIN
EAT_ZOITECHAT
EAT_ALL
EAT_NONE
Used as return values for callbacks.
.. attribute:: __version__
Tuple of (MAJOR_VERSION, MINOR_VERSION)
Generic functions
~~~~~~~~~~~~~~~~~
.. function:: prnt(string)
This function will print string in the current context. It's mainly
useful as a parameter to pass to some other function, since the usual
print statement will have the same results. You have a usage example
above.
This function is badly named because ``"print"`` is a reserved keyword
of the Python language until Python 3.
.. function:: emit_print(event_name, *args)
This function will generate a *print event* with the given arguments. To
check which events are available, and the number and meaning of
arguments, have a look at the :menuselection:`Settings --> Text Events` window.
Here is one example:
.. code-block:: python
zoitechat.emit_print("Channel Message", "John", "Hi there", "@")
With plugin version 1.0+ this function takes keywords for certain attributes such as *time*.
.. function:: command(string)
Execute the given command in the current *context*. This has the same
results as executing a command in the ZoiteChat window, but notice that
the ``/`` prefix is not used. Here is an example:
.. code-block:: python
zoitechat.command("server irc.openprojects.net")
A list of commands is provided here: :ref:`commands_list`.
.. function:: nickcmp(s1, s2)
This function will do an RFC1459 compliant string comparison
and is useful to compare channels and nicknames.
:returns: Returns 0 if they match and less than or greater than 0 if s1 is less than or greather than s2
.. code-block:: python
if zoitechat.nickcmp(nick, "mynick") == 0:
print("They are the same!")
.. function:: strip(text[, length=-1, flags=3])
This function can strip colors and attributes from text.
:param length: -1 for entire string
:param flags:
1: Strip Colors
2: Strip Attributes
3: Strip All
:returns: Stripped String
.. code-block:: python
text = '\00304\002test' # Bold red text
print(text)
print(zoitechat.strip(text, len(text), 1)) # Bold uncolored text
Information retrieving functions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. function:: get_info(type)
Retrieve the information specified by the ``type`` string in the current
context. At the moment of this writing, the following information types
are available to be queried:
- **away:** Away reason or None if you are not away.
- **channel:** Channel name of the current context.
- **charset:** Charset in current context.
- **configdir:** ZoiteChat config directory e.g.: "~/.config/zoitechat".
- **event\_text NAME:** Returns text event string for requested event.
- **gtkwin\_ptr:** Returns hex representation of the pointer to the current Gtk window.
- **host:** Real hostname of the server you connected to.
- **inputbox:** Contents of inputbox.
- **network:** Current network name or None.
- **nick:** Your current nick name.
- **nickserv:** Current networks password or None (**password** is the same with clearer name).
- **modes:** Current channel modes or None.
- **password:** Current networks password or None.
- **server:** Current server name (what the server claims to be) or
None if you are not connected.
- **topic:** Current channel topic.
- **version:** ZoiteChat version number.
- **win\_status:** Returns status of window: 'active', 'hidden', or
'normal'.
Example:
.. code-block:: python
if zoitechat.get_info("network") == 'freenode':
zoitechat.prnt('connected!')
You can also get the format of Text Events by using *event_text* and the event:
.. code-block:: python
print(zoitechat.get_info("event_text Channel Message"))
.. function:: get_prefs(name)
Retrieve the ZoiteChat setting information specified by the ``name``
string, as available by the ``/set`` command.
.. code-block:: python
print("Current preferred nick: " + zoitechat.get_prefs("irc_nick1"))
A list of settings is provided here: :ref:`settings_list`.
On top of that there are a few special preferences:
- **id**: unique server id
- **state_cursor**: location of cursor in input box
.. function:: get_list(type)
With this function you may retrieve a list containing the selected
information from the current context, like a DCC list, a channel list, a
user list, etc. Each list item will have its attributes set dynamically
depending on the information provided by the list type.
The example below is a rewrite of the example provided with ZoiteChat's
plugin API documentation. It prints a list of every DCC transfer
happening at the moment. Notice how similar the interface is to the C
API provided by ZoiteChat.
.. code-block:: python
list = zoitechat.get_list("dcc")
if list:
print("--- DCC LIST ------------------")
print("File To/From KB/s Position")
for i in list:
print("%6s %10s %.2f %d" % (i.file, i.nick, i.cps/1024, i.pos))
Below you will find what each list type has to offer.
List Types
''''''''''
channels
^^^^^^^^
The channels list type gives you access to the channels, queries and
their servers. The following attributes are available in each list item:
- **channel:** Channel or query name.
- **channelkey:** Channel key. (2.9.6+)
- **chanmodes:** Channel modes e.g. beI,k,l. (2.12.2+)
- **chantypes:** Channel types e.g. #!&.
- **context:** A context object, giving access to that channel/server.
- **id:** Unique server id.
- **lag:** Latency in milliseconds.
- **maxmodes:** Max modes per line.
- **network:** Network name to which this channel belongs.
- **nickprefixes:** Nickname prefixes e.g. @%+.
- **nickmodes:** Nickname mode chars e.g. ov.
- **queue:** Number of bytes in the send-queue.
- **server:** Server name to which this channel belongs.
- **users:** Number of users in the channel.
- **type:** Type of context.
- 1: Server
- 2: Channel
- 3: Dialog
- 4: Notices
- 5: SNotices
- **flags:** Bit field of flags:
- 0: Connected
- 1: Connecting
- 2: Away
- 3: End of MOTD (Login Complete)
- 4: Has WHOX
- 5: Has IDMSG
- 6: Join/Parts hidden
- 7: Join/Parts hidden unset
- 8: Beep on Message
- 9: Blink Tray
- 10: Blink Task Bar
- 11: Logging (This and the following are 2.10.0+)
- 12: Logging unset
- 13: Scrollback
- 14: Scrollback unset
- 15: Strip Colors
- 16: Strip Colors unset
dcc
^^^
The dcc list type gives you access to a list of DCC file transfers. The
following attributes are available in each list item:
- **address32:** Address of the remote user (ipv4 address, as an int).
- **cps:** Bytes per second (speed).
- **destfile:** Destination full pathname.
- **file:** Filename.
- **nick:** Nickname of person who the file is from/to.
- **port:** TCP port number.
- **pos:** Bytes sent/received.
- **resume:** Point at which this file was resumed (or zero if it was
not resumed).
- **size:** File size in bytes.
- **status:** DCC status:
- 0: queued
- 1: active
- 2: failed
- 3: done
- 4: connecting
- 5: aborted
- **type:** DCC type:
- 0: send
- 1: receive
- 2: chatrecv
- 3: chatsend
users
^^^^^
The users list type gives you access to a list of users in the current
channel. The following attributes are available in each list item:
- **account:** Account name or None (2.9.6+)
- **away:** Away status.
- **host:** Host name in the form user\@host (or None, if not known).
- **lasttalk:** Time they last talked (2.9.6+)
- **nick:** Nick name.
- **prefix:** Prefix character, .e.g: @ or +. Points to a single char.
- **realname:** Real name.
- **selected:** Selected status in the userlist.
ignore
^^^^^^
The ignore list type gives you access to the current ignored list. The
following attributes are available in each list item:
- **mask:** Ignore mask (for example, "\*\!\*\@\*.aol.com").
- **flags:** Bit field of flags:
- 0: private
- 1: notice
- 2: channel
- 3: ctcp
- 4: invite
- 5: unignore
- 6: nosave
- 7: dcc
notify
^^^^^^
The notify list shows users on your friends list and their status:
- **nick:** Users nickname
- **networks:** Networks they are setup to notify on (None for all)
- **flags:** 0 is offline, 1 is online
- **on:** Time when user last came on (2.9.6+)
- **off:** Time when user last logged off (2.9.6+)
- **seen:** Time when user was last seen (2.9.6+)
Hook functions
~~~~~~~~~~~~~~
These functions allow one to hook into ZoiteChat events.
Parameters
''''''''''
callback
^^^^^^^^
A callback is the function that will be called when the event happens.
The callback supposed to return one of the EAT\_\* `constants <script_python.html#constants-and-attributes>`_,
it is able control how ZoiteChat will proceed after the callback returns. These
are the available constants, and their meanings:
- :data:`EAT_PLUGIN`: Don't let any other plugin receive this event.
- :data:`EAT_ZOITECHAT`: Don't let ZoiteChat treat this event as usual.
- :data:`EAT_ALL`: Eat the event completely.
- :data:`EAT_NONE`: Let everything happen as usual.
.. Note:: Returning ``None`` is the same as returning :data:`EAT\_NONE`.
userdata
^^^^^^^^
The parameter userdata, if given, allows you to pass a custom object to
your callback.
attributes
^^^^^^^^^^
If you create a hook with :func:`hook_server_attrs` or :func:`hook_print_attrs` the last
argument in the callback will be an `Attribute` object.
.. object:: Attribute
.. attribute:: Attribute.time
The time the event occurred (from server-time) or 0
priority
^^^^^^^^
When a priority keyword parameter is accepted, it means that this
callback may be hooked with five different priorities which are
`constants <script_python.html#constants-and-attributes>`_ will define the
order in which your plugin will be called. Most of the time, you won't
want to change its default value (:data:`PRI_NORM`).
word and word\_eol
^^^^^^^^^^^^^^^^^^
These parameters, when available in a command or server callback, are lists of strings
which contain the parameters the user entered for the particular
command. For example, if you executed::
/command NICK Hi there!
- **word[0]** is ``command``
- **word[1]** is ``NICK``
- **word[2]** is ``Hi``
- **word[3]** is ``there!``
- **word\_eol[0]** is ``command NICK Hi there!``
- **word\_eol[1]** is ``NICK Hi there!``
- **word\_eol[2]** is ``Hi there!``
- **word\_eol[3]** is ``there!``
These parameters are also used in print events. When created by these events
they have a completely different meaning though. Text events (:menuselection:`Settings --> Text events`)
have numbered arguments associated with them, these apply to the item in the **word** list.
For example on a "Channel Message" event:
[23:29:26] <@Nick> hello everyone
- **word[0]** is ``Nick``
- **word[1]** is ``hello everyone``
- **word[2]** is ``@``
- **word_eol[0]** is ``Nick hello everyone @``
- **word_eol[1]** is ``hello everyone @``
- **word_eol[2]** is ``@``
.. function:: hook_command(name, callback[, userdata=None, priority=PRI_NORM, help=None])
This function allows you to hook into the name ZoiteChat command. It means
that everytime you type ``/name ...``, ``callback`` will be called.
Parameters ``userdata`` and ``priority`` have their meanings explained
above, and the parameter help, if given, allows you to pass a help text
which will be shown when ``/help name`` is executed. If the command starts
with a period it will not show up in ``/help`` though.
You may also hook an empty string to capture every message a user sends,
either when they hit enter or use ``/say``.
:returns: New Hook Handler
.. code-block:: python
def onotice_cb(word, word_eol, userdata):
if len(word) < 2:
print("Second arg must be the message!")
else:
zoitechat.command("NOTICE @{} {}".format(zoitechat.get_info("channel"), word_eol[1]))
return zoitechat.EAT_ALL
zoitechat.hook_command("ONOTICE", onotice_cb, help="/ONOTICE <message> Sends a notice to all ops")
You may return one of ``EAT_*`` constants in the callback, to control
ZoiteChat's behavior, as explained above.
.. function:: hook_print(name, callback[, userdata=None, priority=PRI_NORM])
This function allows you to register a callback to trap any print
events. The event names are available in the :menuselection:`Settings --> Text Events` window.
Parameters ``userdata`` and ``priority`` have their meanings explained
above.
:param name: event name (see :menuselection:`Settings --> Text Events`)
:returns: New Hook Handler
.. code-block:: python
def youpart_cb(word, word_eol, userdata):
print("You have left channel " + word[2])
return zoitechat.EAT_ZOITECHAT # Don't let ZoiteChat do its normal printing
zoitechat.hook_print("You Part", youpart_cb)
Along with Text Events there are a handfull of *special* events you can hook with this:
- **Open Context**: Called when a new context is created.
- **Close Context**: Called when a context is closed.
- **Focus Tab**: Called when a tab is brought to front.
- **Focus Window**: Called a toplevel window is focused, or the main tab-window is focused by the window manager.
- **DCC Chat Text**: Called when some text from a DCC Chat arrives. It provides these elements in the word list:
- Address
- Port
- Nick
- Message
- **Key Press**: Called when some keys are pressed in the input box. It provides these elements in the word list:
- Key Value
- State Bitfield (shift, capslock, alt)
- String version of the key
- Length of the string (may be 0 for unprintable keys)
.. function:: hook_print_attrs(name, callback[, userdata=None, priority=PRI_NORM])
This function is the same as :func:`hook_print` except its callback will have a new
`Attribute` argument.
:returns: New Hook Handler
.. versionadded:: 1.0
.. code-block:: python
def youpart_cb(word, word_eol, userdata, attributes):
if attributes.time: # Time may be 0 if server-time is not enabled.
print("You have left channel {} at {}".format(word[2], attributes.time))
return zoitechat.EAT_ZOITECHAT
zoitechat.hook_print_attrs("You Part", youpart_cb)
.. function:: hook_server(name, callback[, userdata=None, priority=PRI_NORM])
This function allows you to register a callback to be called when a
certain server event occurs. You can use this to trap ``PRIVMSG``,
``NOTICE``, ``PART``, a server numeric, etc. Parameters ``userdata`` and
``priority`` have their meanings explained above.
You can hook the special event "RAW LINE" to capture all server events.
:returns: New Hook Handler
.. code-block:: python
def kick_cb(word, word_eol, userdata):
print('{} was kicked from {} ({})'.format(word[3], word[2], word_eol[4]))
# Don't eat this event, let other plugins and ZoiteChat see it too
return zoitechat.EAT_NONE
zoitechat.hook_server("KICK", kick_cb)
.. function:: hook_server_attrs(name, callback[, userdata=None, priority=PRI_NORM])
This function is the same as :func:`hook_server` Except its callback will have a new
`Attribute` argument.
:returns: New Hook Handler
.. versionadded:: 1.0
.. code-block:: python
def kick_cb(word, word_eol, userdata, attributes):
if attributes.time: # Time may be 0 if server-time is not enabled.
print('He was kicked at {}'.format(attributes.time))
return zoitechat.EAT_NONE
zoitechat.hook_server_attrs("KICK", kick_cb)
.. function:: hook_timer(timeout, callback[, userdata=None])
This function allows you to register a callback to be called every
timeout milliseconds. Parameters userdata and priority have their
meanings explained above. If the callback returns ``True`` the timer
will repeat otherwise returning ``False`` will stop it.
:returns: New Hook Handler
.. code-block:: python
myhook = None
def stop_cb(word, word_eol, userdata):
global myhook
if myhook is not None:
zoitechat.unhook(myhook)
myhook = None
print("Timeout removed!")
return zoitechat.EAT_ALL
def timeout_cb(userdata):
print("Annoying message every 5 seconds! Type /STOP to stop it.")
return True # Keep the timeout going
myhook = zoitechat.hook_timer(5000, timeout_cb)
zoitechat.hook_command("STOP", stop_cb)
.. function:: hook_unload(callback[, userdata=None])
This function allows you to register a callback to be called when the
plugin is going to be unloaded. Parameters ``userdata`` and ``priority``
have their meanings explained above.
:returns: New Hook Handler
.. code-block:: python
def unload_cb(userdata):
print("We're being unloaded!")
zoitechat.hook_unload(unload_cb)
.. function:: unhook(handler)
Unhooks any hook registered with the hook functions above.
:param handler: Handler returned from :func:`hook_print`, :func:`hook_command`, :func:`hook_server` or :func:`hook_timer`
As of version 1.0 of the plugin hooks from :func:`hook_print` and :func:`hook_command` can be unhooked by their names.
Plugin preferences
~~~~~~~~~~~~~~~~~~
You can use pluginpref to easily store and retrieve settings.
.. function:: set_pluginpref(name, value)
Stores settings in addon\_python.conf in the config dir.
:returns:
- False: Failure
- True: Success
.. versionadded:: 0.9
.. note:: Until the plugin uses different a config file per script it's
recommened to use 'scriptname_settingname' to avoid conflicts.
.. function:: get_pluginpref(name)
This will return the value of the variable of that name. If there is
none by this name it will return ``None``.
:returns: String or Integer of stored setting or None if it does not exist.
.. note:: Strings of numbers and booleans are always returned as Integers.
.. versionadded:: 0.9
.. function:: del_pluginpref(name)
Deletes the specified variable.
:returns:
- False: Failure
- True: Success (or never existing),
.. versionadded:: 0.9
.. function:: list_pluginpref()
Returns a list of all currently set preferences.
:rtype: List of Strings
.. versionadded:: 0.9
Context handling
~~~~~~~~~~~~~~~~
Below you will find information about how to work with contexts.
Context objects
'''''''''''''''
As explained in the Context theory session above, contexts give access
to a specific channel/query/server tab of ZoiteChat. Every function
available in the xchat module will be evaluated in the current context,
which will be specified by ZoiteChat itself before passing control to the
module. Sometimes you may want to work in a specific context, and that's
where `context` objects come into play.
You may create a context object using :func:`get_context` or :func:`find_context`
functions as explained below, or trough the :func:`get_list` function, as explained above.
.. function:: get_context()
:rtype: `context`
.. function:: find_context(server=None, channel=None)
Finds a context based on a channel and servername or if no parameters are given returns
the current (front) context.
:keyword server: if None only looks for channel name
:keyword channel: if None looks for front context of given server
:rtype: `context`
.. code-block:: python
cnc = zoitechat.find_context(channel='#conectiva')
cnc.command('whois niemeyer')
.. object:: context
The context object returned by the functions listed above has these methods:
.. method:: context.set()
Changes the current context to be the one represented by this context object.
.. method:: context.prnt(string)
Does the same as the :func:`prnt` function but in the given context.
.. method:: context.emit_print(event_name, \*args)
Does the same as the :func:`emit_print` function but in the given context.
.. method:: context.command(string)
Does the same as the :func:`command` function but in the given context
.. method:: context.get_info(type)
Does the same as the :func:`get_info` function but in the given context.
.. method:: context.get_list(type)
Does the same as the :func:`get_list` function but in the given context.
--------------
Maintained by: TingPing
Original Author: Gustavo Niemeyer
`gustavo@niemeyer.net <mailto:gustavo@niemeyer.net>`_
+630
View File
@@ -0,0 +1,630 @@
Settings
========
Config Files
------------
Config files are stored as plaintext files (which shouldn't be edited by hand). They are located in:
- Windows: :file:`%APPDATA%\\ZoiteChat`
- Unix: :file:`~/.config/zoitechat`
- Flatpak: :file:`~/.var/app/io.github.Hexchat/config/zoitechat`
- Snap: :file:`~/snap/zoitechat/current/.config/zoitechat`
.. note::
Type the path into your file browser to expand them into a full directory.
Custom directories can be set with the `-d` or `--cfgdir` command line argument.
On Windows there is a portable mode option which makes ZoiteChat store settings inside of a :file:`\\config` subdir within the main installation folder. Though this option should only be used if you must for a portable drive or if you lack administrative priviliges for installation.
Network List
------------
The network list contains a list of networks, basic user information, and per network settings. It can be accessed in :menuselection:`ZoiteChat --> Network List` or by the keyboard shortcut :kbd:`Control-s`.
User Information
~~~~~~~~~~~~~~~~
This is where basic information is set for the user such as nicknames, these will be used as defaults for networks but can be overridden. The Realname field is entirely optional. The username is often used for options like Server and SASL password.
Networks
~~~~~~~~
ZoiteChat comes with a list of default networks but you can easily add your own or edit existing ones. The sorting and names of networks do matter as some commands reference the network by the name here, e.g. :command:`/newserver`. The order they are sorted in this list also determines the order of auto-connecting. To edit the order just select one and hit :kbd:`Shift-up` or :kbd:`Shift-down`.
Servers
^^^^^^^
Per network you can maintain a list of servers in case of one fails. The syntax for these servers *hostname/port*. The port is entirely optional and can be prefixed by *+* to signify SSL. If no port is given the default port used is 6667 and 6697 for SSL.
Your Details
^^^^^^^^^^^^
Unticking *Use global user information* allows unique nicknames or usernames between networks.
Connecting
^^^^^^^^^^
Ticking *Connect to this network automatically* combined with favorite or autojoin channels lets ZoiteChat connect and join chats automatically.
The *Login method* setting controls how ZoiteChat identifies to the server or services. The password field is used by password-based login methods, including server password, NickServ, SASL, and custom connect commands. For custom connect commands, :code:`%p` expands to the saved password.
Tick *Ask for password on connect* to have ZoiteChat prompt for the network password instead of saving it. ZoiteChat asks again after a reconnect.
Passwords are hidden by default. When a password already exists, the field may show :code:`***`; this is only a placeholder, not the real password. Tick *Show password* to load and reveal the current password from the correct storage backend.
Tick *Use system keyring* to store the selected network's password in the operating system keyring instead of the local network configuration file. On Windows this uses Windows Credential Manager. On Unix-like systems this requires libsecret support and a working desktop keyring. If the keyring is locked, ZoiteChat may ask the system to unlock it before the password can be edited.
When *Use system keyring* is disabled, ZoiteChat stores the saved password locally. New local passwords are saved in encrypted local storage when encryption support is available. Local encrypted passwords are less protected than the system keyring and are tied to the current user and configuration directory.
Two migration buttons may appear for existing locally saved passwords:
- *Encrypt saved password* converts an existing local plaintext password to encrypted local storage.
- *Move password to keyring* moves an existing local password into the system keyring and removes the local saved password from the network configuration.
Close the edit window to save changes to the password, login method, and selected password storage mode.
.. seealso::
See the `FAQ <faq.html#why-does-zoitechat-join-channels-before-identifying>`_ if you have trouble identifying before join.
Channel Options
---------------
Some options can be specific to channels. To access these you can right click on any tab or use the command :command:`/chanopt`. Any option other than *0* or *1* is considered *unset* and will use the globally set defaults.
Preferences
-----------
Keyboard Shortcuts
~~~~~~~~~~~~~~~~~~
There are two types of keyboard shortcuts in ZoiteChat hardcoded ones which can be found by looking around in the menu, e.g. :kbd:`Control-s`, and configurable ones in :menuselection:`Settings --> Keyboard Shortcuts`. These have help messages to guide you through setting up custom bindings.
Url Handlers
~~~~~~~~~~~~
Url handlers add alternative browsers to you your right click menu on urls (they do not set the default). They take the syntax::
!program %s or !"C:\Program Files\Program\program.exe" %s
.. note::
If ZoiteChat does not find the executable it will not add it to the menu.
Auto Replace
~~~~~~~~~~~~
Located in :menuselection:`Settings --> Auto Replace` this setting allows you to replace text while typing. The Text column is what it is to detect and the Replace with column is what will replace it. This column accepts color codes from `Text Events <appearance.html#text-events>`_
For Example:
text: --> replace: %C8→%O
This will replace that arrow with a green unicode arrow upon pressing enter or space. The keys that check for replace are defined in :menuselection:`Settings --> Keyboard Shortcuts`. A trick to avoid replacing it :kbd:`Shift-Space` since that is not defined by default.
CTCP Replies
~~~~~~~~~~~~
Custom CTCP replies can be set in :menuselection:`Settings --> CTCP Replies` and accept the same format as `User Commands <commands.html#user-commands>`_ with the addition of *%s* for the nick replying to.
.. note::
To hide the default VERSION reply you must :command:`/set irc_hide_version on`
Set Command
-----------
The set command can be used to change options. The usage is:
:command:`/set option_name [number|string]`
Togglable options take *1* for on and *0* for off but as an alternative you can type:
:command:`/set toggle_option on`
If no value is given it will show the current value of a setting. You can also use wildcards
to find or show multiple values for example
:command:`/set gui_*`
Some options such as *gui_tray* require running this after changing:
:command:`/gui apply`
This can be avoided by using the preferences window instead, which is recommended, also using the gui will warn if a setting requires restart.
.. _settings_list:
List of Settings
----------------
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **away\_auto\_unmark** | Toggle automatically unmarking away before message send. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **away\_omit\_alerts** | Toggle omitting alerts when marked as being away. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **away\_reason** | Default away reason. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **away\_show\_message** | Toggle announcing of away messages. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **away\_show\_once** | Show identical away messages only once. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **away\_size\_max** | How many users can be away in userlist before they are not colored. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **away\_timeout** | How often in seconds to check for max size for colors in userlist. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **away\_track** | Toggle color change for away users in userlist. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **completion\_amount** | How may nicks starting with input there should be before all are shown in text box. |
| | (E.g. if you have 'k' and completion\_amount is set to 6, and there are 6 more people beginning with 'k' in the userlist, then all of the nicks starting with that are shown in the text box. To always cycle nicks, set to 123456 (or any other high number). |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **completion\_auto** | Toggle automatic nick completion. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **completion\_sort** | Toggle nick completion sorting in "last talk" order. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **completion\_suffix** | Suffix to be appended to nicks after completion. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **dcc\_auto\_chat** | Toggle auto accept for DCC chats. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **dcc\_auto\_recv** | How to accept DCC transfers. |
| | |
| | - 0=Ask for confirmation |
| | - 1=Ask for download folder |
| | - 2=Save without interaction |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **dcc\_auto\_resume** | Toggle auto resume of DCC transfers. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **dcc\_blocksize** | The blocksize for DCC transfers. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **dcc\_completed\_dir** | Directory to move completed files to. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **dcc\_dir** | Directory to download files to from DCC. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **dcc\_fast\_send** | Toggle speed up of DCC transfers by not waiting to heard if last part was received before sending next (currently disabled on Win32). |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **dcc\_global\_max\_get\_cps** | Max file transfer speed for all downloads combined in bytes per second. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **dcc\_global\_max\_send\_cps** | Max file transfer speed for all uploads combined in bytes per second. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **dcc\_ip** | DCC IP address to bind to. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **dcc\_ip\_from\_server** | Get address from IRC server. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **dcc\_max\_get\_cps** | Max file transfer speed for one download in bytes per second. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **dcc\_max\_send\_cps** | Max file transfer speed for one upload in bytes per second. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **dcc\_permissions** | What permissions to set on received files. |
| | (It's a CHMOD value in decimal, e.g. to CHMOD a file to 644, which is octal, you need to set dcc\_permissions to 420, which is it's decimal equivalent) |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **dcc\_port\_first** | First DCC port in range (leave ports at 0 for full range). |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **dcc\_port\_last** | Last DCC port in range (leave ports at 0 for full range). |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **dcc\_remove** | Toggle automatic removal of finished/failed DCCs. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **dcc\_save\_nick** | Toggle saving of nicks in filenames. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **dcc\_send\_fillspaces** | Replace spaces in filenames with underscores. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **dcc\_stall\_timeout** | Time in seconds to wait before timing out during a DCC send. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **dcc\_timeout** | Time in seconds to wait before timing out a DCC transfer waiting to be accepted. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **flood\_ctcp\_num** | Number of CTCPs within flood\_ctcp\_time to be considered a flood. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **flood\_ctcp\_time** | Time in seconds for use with flood\_ctcp\_num. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **flood\_msg\_num** | Number of messages within flood\_msg\_time to be considered a flood. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **flood\_msg\_time** | Time in seconds for use with flood\_msg\_num. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_autoopen\_chat** | Toggle auto opening of Direct Chat Window on DCC Chat. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_autoopen\_dialog** | Toggle auto opening of dialog windows. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_autoopen\_recv** | Toggle auto opening of transfer window on DCC Recv. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_autoopen\_send** | Toggle auto opening of transfer window on DCC Send. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_chanlist\_maxusers** | Maximum number of users in channels to be listed in List of Channels. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_chanlist\_minusers** | Minimum number of users in channels to be listed in List of Channels. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_compact** | Toggle compact mode (more or less spacing between user list/channel tree rows). |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_dialog\_height** | New dialog height in pixels. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_dialog\_left** | The X co-ordinance of dialogs when opened. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_dialog\_top** | The Y co-ordinance of dialogs when opened. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_dialog\_width** | New dialog width in pixels. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_hide\_menu** | Hide or unhide menu bar. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_input\_icon** | Toggle user mode icon in the nick box. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_input\_nick** | Toggle the nick box in the input box. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_input\_spell** | Enable or disable spell checking. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_input\_style** | Toggle use of text box colors and fonts in input box. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_join\_dialog** | Toggle join dialog after connect. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_lagometer** | Toggle types of Lag-O-Meters. |
| | |
| | - 0=Off |
| | - 1=Graph |
| | - 2=Text |
| | - 3=Both |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_lang** | Set GUI language. Possible values are from 0 to 50 (Win32 only). |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_mode\_buttons** | Toggle mode buttons. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_pane\_left\_size** | Change size left pane. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_pane\_right\_size** | Change size right pane. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_pane\_divider\_position** | Saves position of divider when channel switcher and user list are on the same side. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_pane\_right\_size\_min** | FIXME |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_quit\_dialog** | Toggle quit dialog. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_slist\_fav** | Toggle showing favorites only in network list. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_slist\_select** | The number of the server to select by default in the server list starting at 0. (E.g. to select the 67th server, set it to 66) |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_slist\_skip** | Toggle server list on startup. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_tab\_chans** | Open channels in tabs instead of windows. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_tab\_dialogs** | Open dialogs in tabs instead of windows. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_tab\_dots** | Toggle dotted lines in the channel tree. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_tab\_icons** | Toggle channel tree icons. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_tab\_layout** | Use treeview or tabs. |
| | |
| | - 0=Tabs |
| | - 2=Treeview |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_tab\_newtofront** | When to focus new tabs. |
| | |
| | - 0=Never |
| | - 1=Always |
| | - 2=Only on requested tabs |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_tab\_pos** | Set position of tabs. |
| | |
| | - 1=Left-Upper |
| | - 2=Left |
| | - 3=Right-Upper |
| | - 4=Right |
| | - 5=Top |
| | - 6=Bottom |
| | - 7=Hidden |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_tab\_server** | Open an extra tab for server messages. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_tab\_small** | Set small tabs. |
| | |
| | - 0=Off |
| | - 1=Small tabs |
| | - 2=Extra small tabs |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_tab\_sort** | Toggle alphabetical sorting of tabs. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_tab\_trunc** | Number or letters to shorten tab names to. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_tab\_utils** | Open utils in tabs instead of windows. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_throttlemeter** | Toggle types of throttle meters. |
| | |
| | - 0=Off |
| | - 1=Graph |
| | - 2=Text |
| | - 3=Both |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_topicbar** | Toggle topic bar. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_tray** | Enable system tray icon. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_tray\_away** | Automatically mark away/back when the tray is toggled. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_tray\_blink** | Toggle tray icon blinking or using static images. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_tray\_close** | Close to tray. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_tray\_minimize** | Minimize to tray. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_tray\_quiet** | Only show tray balloons when hidden or iconified. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_ulist\_buttons** | Toggle userlist buttons. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_ulist\_count** | Toggle displaying user count on top of the user list. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_ulist\_doubleclick** | Command to run upon double click of user in userlist. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_ulist\_hide** | Hides userlist. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_ulist\_icons** | Toggle use of icons instead of text symbols in user list. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_ulist\_pos** | Set userlist position. |
| | |
| | - 1=Left-Upper |
| | - 2=Left-Lower |
| | - 3=Right-Upper |
| | - 4=Right-Lower |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_ulist\_resizable** | Toggle resizable userlist. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_ulist\_show\_hosts** | Toggle user's hosts displaying in userlist. (requires irc_who_join) |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_ulist\_sort** | How to sort users in the userlist. |
| | |
| | - 0=A-Z with Ops first |
| | - 1=A-Z |
| | - 2=A-Z with Ops last |
| | - 3=Z-A |
| | - 4=Unsorted |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_ulist\_style** | Toggle use of text box colors and fonts in userlist. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_url\_mod** | How to handle URLs when clicked. (And what to hold.) |
| | |
| | - 0=Left Click Only |
| | - 1=Shift |
| | - 2=Caps Lock |
| | - 4=CTRL |
| | - 8=ALT |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_usermenu** | Toggle editable usermenu. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_win\_height** | Main window height in pixels. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_win\_left** | The X co-ordinance of main window when opened. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_win\_modes** | Show channel modes in title bar. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_win\_save** | Toggles saving of state on exit. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_win\_state** | Default state of the main window. |
| | |
| | - 0=Not Maximized |
| | - 1=Maximized |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_win\_swap** | Swap the middle and left panes (allows side-by-side userlist/tree). |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_win\_top** | The Y co-ordinance of main window when opened. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_win\_ucount** | Show number of users in title bar. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **gui\_win\_width** | Main window width in pixels. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **identd** | Toggle internal IDENTD (Win32 only). |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **input\_balloon\_chans** | Show tray balloons on channel messages. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **input\_balloon\_hilight** | Show tray balloons on highlighted messages. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **input\_balloon\_priv** | Show tray balloons on private messages. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **input\_balloon\_time** | How long balloon messages should be displayed. (2.8.8+) |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **input\_beep\_chans** | Toggle beep on channel messages. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **input\_beep\_hilight** | Toggle beep on highlighted messages. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **input\_beep\_priv** | Toggle beep on private messages. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **input\_command\_char** | Character used to execute commands. |
| | (E.g. if set to '[' then you would use commands like '[me jumps around') |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **input\_filter\_beep** | Toggle filtering of beeps sent by others. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **input\_flash\_chans** | Toggle whether or not to flash taskbar on channel messages. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **input\_flash\_hilight** | Toggle whether or not to flash taskbar on highlighted messages. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **input\_flash\_priv** | Toggle whether or not to flash taskbar on private messages. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **input\_perc\_ascii** | Toggle interpreting of %nnn as ASCII value. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **input\_perc\_color** | Toggle interpreting of %C, %B as color, bold, etc. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **input\_tray\_chans** | Blink tray icon on channel messages. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **input\_tray\_hilight** | Blink tray icon on highlighted messages. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **input\_tray\_priv** | Blink tray icon on private messages. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **irc\_auto\_rejoin** | Toggle auto rejoining when kicked. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **irc\_reconnect\_rejoin** | Toggle auto rejoining on auto reconnect. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **irc\_ban\_type** | The default ban type to use for all bans. (requres irc_who_join) |
| | |
| | - 0=\*!\*\@\*.host |
| | - 1=\*!\*\@domain |
| | - 2=\*!\*user\@\*.host |
| | - 3=\*!\*user\@domain |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **irc\_conf\_mode** | Toggle hiding of join, part and quit messages. (`More info <http://toxin.jottit.com/xchat_tips_&_tricks#09>`_) |
| | |
| | - 0=Show join/part/quits |
| | - 1=Hide join/part/quits |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **irc\_extra\_hilight** | Extra words to highlight on. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **irc\_hide\_version** | Toggle hiding of VERSION reply. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **irc\_id\_ntext** | $4 in the channel message, channel message hilight and private message events if unidentified. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **irc\_id\_ytext** | $4 in the channel message, channel message hilight and private message events if identified. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **irc\_invisible** | Toggle invisible mode (+i). |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **irc\_join\_delay** | How long to delay auto-joining a channel after connect. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **irc\_logging** | Toggle logging. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **irc\_logmask** | Mask used to create log filenames (strftime details: `Windows <http://msdn.microsoft.com/en-us/library/fe06s4ak%28v=vs.100%29.aspx>`_ `Unix <http://linux.die.net/man/3/strftime>`_). |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **irc\_nick1** | First choice nick. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **irc\_nick2** | Second choice nick. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **irc\_nick3** | Third choice nick. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **irc\_nick\_hilight** | What nicks to highlight when they talk. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **irc\_notice\_pos** | Placement of Notices: |
| | |
| | - 0 = Automatic |
| | - 1 = Open extra (notices) tab |
| | - 2 = Always place in front tab |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **irc\_no\_hilight** | Nicks not to highlight on. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **irc\_part\_reason** | Default reason when leaving channel. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **irc\_quit\_reason** | Default quit reason. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **irc\_raw\_modes** | Toggle RAW channel modes. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **irc\_real\_name** | Real name to be sent to server. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **irc\_servernotice** | Toggle receiving of server notices. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **irc\_skip\_motd** | Toggle skipping of server MOTD. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **irc\_user\_name** | Username to be sent to server. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **irc\_wallops** | Toggle receiving wallops. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **irc\_who\_join** | Toggle running WHO after joining channel. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **irc\_whois\_front** | Toggle whois results being sent to currently active tab. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **net\_auto\_reconnect** | Toggle auto reconnect to server. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **net\_auto\_reconnectonfail** | Toggle auto reconnect upon failed connection. (Unix only command, not available on Windows) |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **net\_bind\_host** | Network address to bind ZoiteChat to. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **net\_ping\_timeout** | How long server ping has to be to timeout. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **net\_proxy\_auth** | Toggle proxy authentication. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **net\_proxy\_host** | Proxy host to use. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **net\_proxy\_pass** | Password to use if proxy authentication is turned on. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **net\_proxy\_port** | Port to use for proxy host. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **net\_proxy\_type** | Type of proxy to use. |
| | |
| | - 0=Disabled |
| | - 1=Wingate |
| | - 2=Socks4 |
| | - 3=Socks5 |
| | - 4=HTTP |
| | - 5=MS Proxy (ISA) |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **net\_proxy\_use** | What to use proxies for (if set). |
| | |
| | - 0=All |
| | - 1=IRC Only |
| | - 2=DCC Only |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **net\_proxy\_user** | Username to use if proxy authentication is turned on. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **net\_reconnect\_delay** | How many seconds to wait before reconnection. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **net\_throttle** | Toggle flood protection (to keep from getting kicked). |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **notify\_timeout** | How often in seconds to check for users in your notify list. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **notify\_whois\_online** | Toggle performing WHOIS on users on your notify list when they come online. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **perl\_warnings** | Toggle perl warnings. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **sound\_dir** | Directory where sounds are located. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **stamp\_log** | Toggle timestamps in logs. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **stamp\_log\_format** | Format to use for log timestamps (strftime details: `Windows <http://msdn.microsoft.com/en-us/library/fe06s4ak%28v=vs.100%29.aspx>`_ `Unix <http://linux.die.net/man/3/strftime>`_). |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **stamp\_text** | Toggle timestamps in text box. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **stamp\_text\_format** | Format to use for timestamps in textbox (strftime details: `Windows <http://msdn.microsoft.com/en-us/library/fe06s4ak%28v=vs.100%29.aspx>`_ `Unix <http://linux.die.net/man/3/strftime>`_). |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **text\_autocopy\_color** | Toggle automatic copying of color information. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **text\_autocopy\_stamp** | Toggle automatic copying of time stamps. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **text\_autocopy\_text** | Toggle automatic copying of selected text. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **text\_background** | Sets the background image for text box. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **text\_color\_nicks** | Toggle colored nicks. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **text\_font** | All fonts to be used (main and alternative fonts combined, shouldn't be edited manually). |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **text\_font\_main** | Primary font to be used. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **text\_font\_alternative** | Alternative fonts to be used for glyphs not supported by the primary font. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **text\_indent** | Toggle text indentation. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **text\_max\_indent** | Max pixels to indent text with. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **text\_max\_lines** | Max number or scrollback lines. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **text\_replay** | Reloads conversation buffers on next startup. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **text\_search\_case\_match** | Toggle performing a case-sensitive search. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **text\_search\_backward** | Toggle searching from newest text line to the oldest. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **text\_search\_highlight\_all** | Toggle highlighting all occurences and underlining of the current occurence. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **text\_search\_follow** | Toggle search for newly arriving messages. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **text\_search\_regexp** | Toggle regarding search string as a regular expression. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **text\_show\_marker** | Toggle red marker line feature. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **text\_show\_sep** | Toggle separator line. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **text\_spell\_langs** | List of languages to have spelling for, by language codes, separated by commas. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **text\_stripcolor\_msg** | Toggle stripping colors from messages. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **text\_stripcolor\_replay** | Toggle stripping colors from scrollback. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **text\_stripcolor\_topic** | Toggle stripping colors from topic. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **text\_thin\_sep** | Use thin separator line instead of thick line. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **text\_transparent** | Toggle transparent background. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **text\_wordwrap** | Toggle wordwrap. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **url\_grabber** | Toggle URL grabber. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **url\_grabber\_limit** | Limit the number of URLs handled by the url grabber. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| **url\_logging** | Toggle logging URLs to *<config>/url.log*. |
+------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+147
View File
@@ -0,0 +1,147 @@
Tips & Tricks
=============
Spell Check
-----------
Windows
~~~~~~~
.. image:: _static/img/tips_spellcheck.png
ZoiteChat comes with spellcheck already installed. To use additional dictionaries, add them to Windows:
1. In the Start menu, go to :menuselection:`Settings -> Time & Language -> Region & Language`.
2. Add new language(s).
3. Restart ZoiteChat.
Unix
~~~~
ZoiteChat comes with spellcheck already installed and enabled by default on most platforms.
.. note:: If it's missing, install enchant and your spelling dictionaries via your package manager (e.g. hunspell-en-us for English). Then make sure to enable spelling under :menuselection:`Settings --> Preferences --> Interface --> Input box`.
You can list your languages in :menuselection:`Settings --> Preferences --> Interface --> Input box` by their language codes (i.e. de_DE for german), separated by commas.
Localization
------------
In order to start ZoiteChat in a different language (for which a translation exists `here <https://www.transifex.com/projects/p/zoitechat/>`_) you can use the regional settings of Windows, or set the LC_ALL user environmental variable. The value of the variable must be the two letter country code for your country. If in doubt, have a look at the share\locale folder. You have to restart ZoiteChat for the changes to apply.
You can also use a batch file to affect only ZoiteChat:
.. code-block:: bat
@echo off
set LC_ALL=en
start zoitechat.exe
This sets the language to English. You may use *fr* for French, *de* for German, etc. Save the code above as :file:`run.bat`, and copy it to the ZoiteChat install folder. You can then start ZoiteChat in the desired language by running the batch file.
Special Glyphs
--------------
There are many symbols which may not be supported by the main font you selected to use in ZoiteChat, especially Asian glyphs and special characters, like a peace sign. In this case, you'll see "lego blocks" instead of them.
To circumvent this, you need to have alternative fonts for glyphs not supported by your current font. On Unix this is handled automatically. On Windows you can specify them in :menuselection:`Settings --> Preferences --> Chatting --> Advanced --> Alternative fonts`. By default, it is set to *Arial Unicode MS,Segoe UI Emoji,Lucida Sans Unicode,Meiryo,Symbola,Unifont*, which should cover most characters (note that Unifont does not come with Windows).
There are many available fonts that try to cover most of unicode:
- `Unifont <http://unifoundry.com/unifont.html>`_
- `Symbola <http://users.teilar.gr/~g1951d/>`_
- `Quivira <http://www.quivira-font.com/>`_
In case you still get lego blocks, you'll need to add additional fonts to the list which support those obscure glyphs. Feel free to extend the list. You only need to specify font names, other info (such as size, weight, style etc.) should be omitted, otherwise those entries will be ignored. All font names must be separated by a comma and there mustn't be spaces before and/or after commas.
Please bear in mind that for some reason certain fonts that can display a certain glyph when used as the main font may not work when specified as an alternative font so you might have to play around it a bit.
Client Certificates
-------------------
Client certificates identify you to IRC network services. This is separate from accepting invalid server certificates.
Use the network list dialog to configure client certification for each network:
#. Open :menuselection:`ZoiteChat --> Network List`.
#. Select a network and click :command:`Edit`.
#. In the client SSL cert area, use the buttons shown for your current state:
- Use :command:`Generate client SSL cert` to create a new certificate for that network, or :command:`Import client SSL cert` to copy an existing one.
- If a certificate already exists, use :command:`Client SSL cert info` to inspect it or :command:`Delete cert` to remove it.
On networks that support it you can use SASL EXTERNAL in the network list. If a network does not support this but does support normal SASL usually that would be the better option.
4. Save the network settings and reconnect.
Note on Custom Server Certificates
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
On Windows it is possible to edit *cert.pem* file in ZoiteChat main installation directory and add custom certificate there. But this method isn't very effective as *cert.pem* is overwritten each time ZoiteChat installer is used.
Notice Placement
----------------
Other than channel messages and private messages, IRC has a notice type of message. This is intended to be used as a reply, something that will not cause the other client to send any acknowledgement back. When ZoiteChat displays these messages, it shows them in a tab that it figures is appropriate.
Why replies from ChanServ may not appear in the current tab
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
When ZoiteChat decides where to print a notice, it does so in the following order:
1. In a query window you have with that user
2. In the front tab, if the tab is a channel, the other user is on that channel, and you are on the correct network
3. In the last joined channel you have in common with the other user
4. The current tab, if you are on the same network
5. The last tab you looked at that shares the correct network with the other user
This means that if you issue a :command:`/cs info #yourchannel` from your channel, the reply may show up elsewhere if ChanServ isn't in your channel, but is in some other channel.
How to make notices show up in a consistent location
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The simplest method is to set the location in :menuselection:`Settings --> Preferences --> Channel switcher --> Placement of notices`, and select "in an extra tab" or "in the front tab". The former will cause all server notices to go into a (snotices) tab, and all user notices to go into a (notices) tab. The latter will always print the notices where you are, this can cause odd positioning of channel notices but you will never miss them.
If you know who will notice you before hand, you can simply query the user before they notice you. This way, all notices from that user will show up in the query tab. In the case of ChanServ, this may allow an easier archive of commands you have done anyway.
For other locations, a separate script would be required. While not currently implemented, it would be possible with a script to treat all notices like private messages (open a new query window when received), or place them in a specific existing tab, such as the server tab. At this point, the choice is up to you (or whoever designs the script).
How the marker line works
-------------------------
The marker line is a very useful tool to keep track of what you have and have not read in a channel but it's behavior is non-obvious at times. It just follows a few simple rules though.
A line is created when new information is printed in a context that is not currently visible. This means the window is in the background, another tab is selected, or you are scrolled up.
This line by design only automatically resets when it is seen. One common issue here is that the marker line is at the very top of your scrollback so you very unlikely to see it. This can happen with bnc playback for example where you get a lot of messages at once.
ZoiteChat has two shortcuts to reset the marker line also. Ctrl+M will reset the the marker line directly. Ctrl+Shift+M will scroll to where the marker was which is quite useful if you actually care about the scrollback.
Once a marker line is "reset" it does not instantly get created at the bottom it will only be created if it matches the conditions mentioned above (not being visible).
Tor
---
1. Find if the network you wish to connect to allows connections from the Onion Network (check their official website and the Message of The Day).
2. Get Tor working. Refer to the tutorials from the `official Tor support center <https://support.torproject.org/>`_.
3. Set up proxy in :menuselection:`Settings --> Preferences --> Network Setup`.
Example (with defaults):
.. image:: _static/img/tips_tor_1.png
4. Setup the network in :menuselection:`ZoiteChat --> Network List`. (Note: use only the updated information from the official website of the IRC network you wish to connect to)
Example:
.. image:: _static/img/tips_tor_2.png
Twitch
------
Twitch.tv uses irc for chat so you can use a regular client for chat but it is a very customized irc that has some extra requirements.
In the Network List add a new network and for the server use *irc.chat.twitch.tv* with SSL. You must have your nickname match your twitch account. For the login method choose *Server Password* and generate a password on this website `<http://twitchapps.com/tmi>`_
To enhance your experience I recommend using the `twitch.lua <https://github.com/TingPing/plugins/blob/master/HexChat/twitch.lua>`_ script.
+13 -8
View File
@@ -7,14 +7,13 @@
"command": "zoitechat", "command": "zoitechat",
"finish-args": [ "finish-args": [
"--share=ipc", "--share=ipc",
"--socket=x11", "--socket=wayland",
"--socket=fallback-x11",
"--share=network", "--share=network",
"--socket=pulseaudio", "--socket=pulseaudio",
"--filesystem=xdg-download", "--filesystem=xdg-download",
"--filesystem=xdg-data/themes:ro", "--filesystem=xdg-data/themes:ro",
"--filesystem=xdg-data/icons:ro", "--filesystem=xdg-data/icons:ro",
"--filesystem=~/.themes:ro",
"--filesystem=~/.icons:ro",
"--filesystem=xdg-run/tray-icon:create", "--filesystem=xdg-run/tray-icon:create",
"--env=GTK_CSD=1", "--env=GTK_CSD=1",
"--talk-name=org.freedesktop.Notifications", "--talk-name=org.freedesktop.Notifications",
@@ -34,7 +33,6 @@
} }
}, },
"modules": [ "modules": [
"shared-modules/dbus-glib/dbus-glib.json",
"shared-modules/lua5.4/lua-5.4.json", "shared-modules/lua5.4/lua-5.4.json",
"shared-modules/libcanberra/libcanberra.json", "shared-modules/libcanberra/libcanberra.json",
"shared-modules/libayatana-appindicator/libayatana-appindicator-gtk3.json", "shared-modules/libayatana-appindicator/libayatana-appindicator-gtk3.json",
@@ -45,17 +43,23 @@
"buildsystem": "meson", "buildsystem": "meson",
"sources": [ "sources": [
{ {
"type": "git", "type": "archive",
"url": "https://github.com/pavouk/lgi.git", "url": "https://github.com/pavouk/lgi/archive/c9b8e4473c6421f2a215d8c06c0d94b86eb0b26a.tar.gz",
"commit": "c9b8e4473c6421f2a215d8c06c0d94b86eb0b26a" "sha256": "db67b2b7ee89fa566f783486d56be7203552a997bc55f35020b57dd2776b9943"
} }
] ]
}, },
{ {
"name": "zoitechat", "name": "zoitechat",
"buildsystem": "meson", "buildsystem": "meson",
"run-tests": true,
"test-rule": "",
"test-commands": [
"meson test --print-errorlogs 'Emoji Data Tests' 'Emoji Font Tests'"
],
"config-opts": [ "config-opts": [
"-Ddbus-service-use-appid=true", "-Ddbus-service-use-appid=true",
"-Dinstall-docs=false",
"-Dwith-perl=perl", "-Dwith-perl=perl",
"-Dwith-python=python3", "-Dwith-python=python3",
"-Dwith-lua=lua" "-Dwith-lua=lua"
@@ -67,7 +71,8 @@
"/share/man" "/share/man"
], ],
"post-install": [ "post-install": [
"install -d /app/extensions" "install -d /app/extensions",
"cmp ../data/fonts/NotoColorEmoji.ttf /app/share/fonts/zoitechat/NotoColorEmoji.ttf"
], ],
"sources": [ "sources": [
{ {
+2 -2
View File
@@ -13,8 +13,8 @@
"sources": [ "sources": [
{ {
"type": "archive", "type": "archive",
"url": "https://www.cpan.org/src/5.0/perl-5.40.1.tar.xz", "url": "https://www.cpan.org/src/5.0/perl-5.42.2.tar.xz",
"sha256": "dfa20c2eef2b4af133525610bbb65dd13777ecf998c9c5b1ccf0d308e732ee3f" "sha256": "0a585eeb9e363c0f80482ddb3571625250c2c86aeb408853e8ea50805cfb14bb"
} }
] ]
} }
+66 -6
View File
@@ -1,6 +1,6 @@
project('zoitechat', 'c', project('zoitechat', 'c',
version: '2.18.0~pre6', version: run_command('cat', 'VERSION', check: true).stdout().strip(),
meson_version: '>= 0.55.0', meson_version: '>= 0.59.0',
default_options: [ default_options: [
'c_std=c17', 'c_std=c17',
'buildtype=debugoptimized', 'buildtype=debugoptimized',
@@ -13,12 +13,15 @@ gnome = import('gnome')
cc = meson.get_compiler('c') cc = meson.get_compiler('c')
libgio_dep = dependency('gio-2.0', version: '>= 2.36.0') # The relocatable macOS runtime sets the GIO TLS database explicitly.
libgio_dep = dependency('gio-2.0',
version: host_machine.system() == 'darwin' ? '>= 2.60.0' : '>= 2.36.0')
libgmodule_dep = dependency('gmodule-2.0') libgmodule_dep = dependency('gmodule-2.0')
libcanberra_dep = dependency('libcanberra', version: '>= 0.22', libcanberra_dep = dependency('libcanberra', version: '>= 0.22',
required: get_option('libcanberra')) required: get_option('libcanberra'))
dbus_glib_dep = dependency('dbus-glib-1', required: get_option('dbus')) dbus_dep = dependency('gio-2.0', required: get_option('dbus'))
libsecret_dep = dependency('libsecret-1', required: false)
global_deps = [] global_deps = []
if cc.get_id() == 'msvc' if cc.get_id() == 'msvc'
@@ -28,18 +31,30 @@ else
required: get_option('tls')) required: get_option('tls'))
endif endif
gtk_mac_dep = disabler()
if host_machine.system() == 'darwin' and get_option('gtk-frontend')
gtk_mac_dep = dependency('gtk-mac-integration-gtk3', required: get_option('mac-integration'))
endif
config_h = configuration_data() config_h = configuration_data()
config_h.set('HAVE_GTK_MAC', host_machine.system() == 'darwin' and gtk_mac_dep.found())
config_h.set10('ZOITECHAT_INSTALL_EMOJI_FONT', get_option('install-emoji-font'))
config_h.set_quoted('PACKAGE_VERSION', meson.project_version()) config_h.set_quoted('PACKAGE_VERSION', meson.project_version())
config_h.set_quoted('PACKAGE_NAME', meson.project_name()) config_h.set_quoted('PACKAGE_NAME', meson.project_name())
config_h.set_quoted('GETTEXT_PACKAGE', 'zoitechat') config_h.set_quoted('GETTEXT_PACKAGE', 'zoitechat')
config_h.set_quoted('LOCALEDIR', join_paths(get_option('prefix'), config_h.set_quoted('LOCALEDIR', join_paths(get_option('prefix'),
get_option('datadir'), 'locale')) get_option('datadir'), 'locale'))
config_h.set_quoted('ZOITECHAT_DOCDIR',
join_paths(get_option('prefix'), get_option('datadir'), 'doc', 'zoitechat', 'html')
)
config_h.set10('ENABLE_NLS', true) config_h.set10('ENABLE_NLS', true)
config_h.set_quoted('ZOITECHAT_DATADIR', join_paths(get_option('prefix'), get_option('datadir')))
# Optional features # Optional features
config_h.set('USE_OPENSSL', libssl_dep.found()) config_h.set('USE_OPENSSL', libssl_dep.found())
config_h.set('USE_LIBCANBERRA', libcanberra_dep.found()) config_h.set('USE_LIBCANBERRA', libcanberra_dep.found())
config_h.set('USE_DBUS', dbus_glib_dep.found()) config_h.set('USE_DBUS', dbus_dep.found())
config_h.set('HAVE_LIBSECRET', libsecret_dep.found())
config_h.set('USE_PLUGIN', get_option('plugin')) config_h.set('USE_PLUGIN', get_option('plugin'))
config_h.set('USE_GTK_FRONTEND', get_option('gtk-frontend')) config_h.set('USE_GTK_FRONTEND', get_option('gtk-frontend'))
@@ -96,6 +111,12 @@ global_cflags = [
'-DGLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_2_36', '-DGLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_2_36',
'-DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_36', '-DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_36',
] ]
if host_machine.system() == 'darwin'
global_cflags = [
'-DGLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_2_60',
'-DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_60',
]
endif
test_cflags = [ test_cflags = [
'-funsigned-char', '-funsigned-char',
'-Wno-conversion', '-Wno-conversion',
@@ -156,8 +177,47 @@ foreach ldflag : test_ldflags
global_ldflags += ldflag global_ldflags += ldflag
endif endif
endforeach endforeach
if host_machine.system() == 'darwin'
# Leave room for install_name_tool to rewrite packaged dependencies.
global_ldflags += '-Wl,-headerpad_max_install_names'
endif
add_project_link_arguments(global_ldflags, language: 'c') add_project_link_arguments(global_ldflags, language: 'c')
if get_option('install-docs')
docs_python = find_program('python3', 'python')
docs_sphinx = run_command(docs_python, '-c', 'import sphinx', check: false)
if docs_sphinx.returncode() != 0
error('Sphinx is required to build the local HTML documentation. Install Sphinx or configure with -Dinstall-docs=false.')
endif
docs_source_dir = join_paths(meson.source_root(), 'docs')
docs_output_dir = join_paths(meson.build_root(), 'docs-html')
docs_build_script = join_paths(docs_source_dir, 'build-docs.py')
docs_install_script = join_paths(docs_source_dir, 'install-docs.py')
docs_target = custom_target(
'html-documentation',
output: 'html-documentation.stamp',
command: [
docs_python,
docs_build_script,
docs_source_dir,
docs_output_dir,
'@OUTPUT@',
],
build_by_default: true,
build_always_stale: true,
)
meson.add_install_script(
docs_python,
docs_install_script,
docs_target,
docs_output_dir,
get_option('datadir'),
)
endif
subdir('src') subdir('src')
if get_option('plugin') if get_option('plugin')
subdir('plugins') subdir('plugins')
@@ -181,7 +241,7 @@ if meson.version().version_compare('>= 0.55.0')
'GTK Frontend': get_option('gtk-frontend') ? 'enabled (GTK+ 3.22+)' : 'disabled', 'GTK Frontend': get_option('gtk-frontend') ? 'enabled (GTK+ 3.22+)' : 'disabled',
'TLS (openssl)': libssl_dep.found(), 'TLS (openssl)': libssl_dep.found(),
'Plugin Support': get_option('plugin'), 'Plugin Support': get_option('plugin'),
'DBus Support': dbus_glib_dep.found(), 'DBus Support': dbus_dep.found(),
'libcanberra': libcanberra_dep.found(), 'libcanberra': libcanberra_dep.found(),
}, section: 'Features') }, section: 'Features')
+11 -4
View File
@@ -23,6 +23,10 @@ option('appindicator', type: 'feature', value: 'auto',
description: 'Use Ayatana/AppIndicator-based tray backend for GTK frontend (non-Windows only)' description: 'Use Ayatana/AppIndicator-based tray backend for GTK frontend (non-Windows only)'
) )
# macOS only; ignored by other platforms.
option('mac-integration', type: 'feature', value: 'auto',
description: 'Native macOS menu bar, Dock and application lifecycle')
# Install options # Install options
option('dbus-service-use-appid', type: 'boolean', value: false, option('dbus-service-use-appid', type: 'boolean', value: false,
description: 'Rename dbus service to match app-id, required for Flatpak' description: 'Rename dbus service to match app-id, required for Flatpak'
@@ -33,6 +37,12 @@ option('install-appdata', type: 'boolean',
option('install-plugin-metainfo', type: 'boolean', value: false, option('install-plugin-metainfo', type: 'boolean', value: false,
description: 'Installs metainfo files for enabled plugins, useful when distros create split packages' description: 'Installs metainfo files for enabled plugins, useful when distros create split packages'
) )
option('install-emoji-font', type: 'boolean', value: true,
description: 'Install the bundled Noto Color Emoji font instead of using system emoji fonts'
)
option('install-docs', type: 'boolean', value: false,
description: 'Build and install the local HTML documentation'
)
# Plugins # Plugins
option('with-checksum', type: 'boolean', option('with-checksum', type: 'boolean',
@@ -44,7 +54,7 @@ option('with-exec', type: 'boolean',
option('with-fishlim', type: 'boolean', option('with-fishlim', type: 'boolean',
description: 'Fish encryption plugin, requires openssl' description: 'Fish encryption plugin, requires openssl'
) )
option('with-lua', type: 'string', value: 'luajit', option('with-lua', type: 'string', value: 'lua-5.4',
description: 'Lua scripting plugin, value is pkg-config name to use or "false"' description: 'Lua scripting plugin, value is pkg-config name to use or "false"'
) )
option('with-perl', type: 'string', value: 'perl', option('with-perl', type: 'string', value: 'perl',
@@ -59,9 +69,6 @@ option('with-sysinfo', type: 'boolean',
option('with-upd', type: 'boolean', option('with-upd', type: 'boolean',
description: 'Update plugin, Windows only' description: 'Update plugin, Windows only'
) )
option('with-winamp', type: 'boolean',
description: 'Winamp plugin, Windows only'
)
option('with-perl-legacy-api', type: 'boolean', value: false, option('with-perl-legacy-api', type: 'boolean', value: false,
description: 'Enables the legacy IRC perl module for compatibility with old scripts' description: 'Enables the legacy IRC perl module for compatibility with old scripts'
) )
+112
View File
@@ -0,0 +1,112 @@
Name: zoitechat
Version: %(cat VERSION)
Release: %autorelease
Summary: HexChat-based IRC client
License: GPL-2.0-or-later WITH cryptsetup-OpenSSL-exception
URL: https://github.com/ZoiteChat/zoitechat
Source0: %{url}/archive/refs/tags/%{name}-%{version}.tar.gz
BuildRequires: desktop-file-utils
BuildRequires: gcc
BuildRequires: gettext
BuildRequires: libappstream-glib
BuildRequires: meson >= 0.55.0
BuildRequires: perl
BuildRequires: perl-devel
BuildRequires: python3
BuildRequires: python3-cffi
BuildRequires: python3-sphinx
BuildRequires: publicsuffix-list
BuildRequires: xwayland-run
BuildRequires: weston
BuildRequires: pkgconfig(ayatana-appindicator3-0.1)
BuildRequires: pkgconfig(dbus-glib-1)
BuildRequires: pkgconfig(gio-2.0) >= 2.36.0
BuildRequires: pkgconfig(gmodule-2.0)
BuildRequires: pkgconfig(gtk+-3.0) >= 3.22
BuildRequires: pkgconfig(fontconfig) >= 2.13
BuildRequires: pkgconfig(pangoft2) >= 1.44
BuildRequires: pkgconfig(iso-codes)
BuildRequires: pkgconfig(libarchive)
BuildRequires: pkgconfig(libcanberra) >= 0.22
BuildRequires: pkgconfig(libpci)
BuildRequires: pkgconfig(lua)
BuildRequires: pkgconfig(openssl) >= 0.9.8
BuildRequires: pkgconfig(python3)
Requires: hicolor-icon-theme
Requires: iso-codes
%package devel
Summary: Development files for ZoiteChat plugins
Requires: %{name}%{?_isa} = %{version}-%{release}
%description devel
Development files for building ZoiteChat plugins.
%description
ZoiteChat is a HexChat-based IRC client for Windows and UNIX-like operating
systems.
%prep
%autosetup -C
%build
%meson \
-Dinstall-docs=true \
-Dtext-frontend=false \
-Dwith-checksum=true \
-Dwith-fishlim=true \
-Dwith-lua=lua \
-Dwith-perl=perl \
-Dwith-python=python3 \
-Dwith-sysinfo=true \
-Dinstall-appdata=true \
-Dinstall-plugin-metainfo=true
%meson_build
%install
%meson_install
%find_lang %{name}
%check
desktop-file-validate %{buildroot}%{_datadir}/applications/net.zoite.Zoitechat.desktop
appstream-util validate-relax --nonet %{buildroot}%{_datadir}/metainfo/net.zoite.Zoitechat.appdata.xml
appstream-util validate-relax --nonet %{buildroot}%{_datadir}/metainfo/net.zoite.Zoitechat*.metainfo.xml
xwfb-run -- /usr/bin/meson test -C %{_vpath_builddir} --num-processes %{_smp_build_ncpus} --print-errorlogs \
"Emoji Data Tests" \
"Emoji Font Tests" \
"Emoji UI Tests" \
"Theme Manager Dispatch Routing Tests" \
"Validate net.zoite.Zoitechat.desktop" \
"Validate translations" \
"Fishlim Tests"
%files -f %{name}.lang
%license COPYING
%doc readme.md troubleshooting.md
%{_datadir}/fonts/zoitechat/
%{_datadir}/doc/zoitechat/fonts/
%{_bindir}/zoitechat
%{_datadir}/applications/net.zoite.Zoitechat.desktop
%{_datadir}/dbus-1/services/org.zoitechat.service.service
%{_datadir}/icons/hicolor/48x48/apps/net.zoite.Zoitechat.png
%{_datadir}/icons/hicolor/scalable/apps/net.zoite.Zoitechat.svg
%{_datadir}/metainfo/net.zoite.Zoitechat.appdata.xml
%{_datadir}/metainfo/net.zoite.Zoitechat*.metainfo.xml
%{_datadir}/doc/zoitechat/html/
%dir %{_libdir}/zoitechat
%dir %{_libdir}/zoitechat/plugins
%dir %{_libdir}/zoitechat/python
%{_libdir}/zoitechat/plugins/*.so
%{_libdir}/zoitechat/python/*.py
%{_mandir}/man1/zoitechat.1*
%files devel
%{_includedir}/zoitechat-plugin.h
%{_libdir}/pkgconfig/zoitechat-plugin.pc
%changelog
%autochangelog
+15
View File
@@ -0,0 +1,15 @@
brew "meson"
brew "ninja"
brew "pkgconf"
brew "python"
brew "gettext"
brew "gtk+3"
brew "gtk-mac-integration"
brew "libarchive"
brew "openssl@3"
brew "lua"
brew "librsvg"
brew "glib-networking"
brew "adwaita-icon-theme"
brew "enchant"
brew "ca-certificates"
+417
View File
@@ -0,0 +1,417 @@
# Native macOS build and distribution
This builds GTK3 with the native Quartz backend, macOS menus/Dock integration,
UserNotifications, translations, bundled emoji, TLS, image loaders and supported
native plugins. It creates a self-contained `ZoiteChat.app`; end users do not
need Homebrew, XQuartz or Python. No system-wide ZoiteChat installation occurs.
The source changes are conditional on Darwin. Windows and Linux retain their
existing notification backends and feature defaults. This adds a separate
macOS workflow; it does not replace existing workflows.
## 1. Install build prerequisites
Use a Mac with a currently supported macOS release and native Homebrew. On Apple
Silicon, use ARM Homebrew (`/opt/homebrew`), not Intel Homebrew under Rosetta.
Intel builds use Intel Homebrew (`/usr/local`). Install Xcode command-line tools
and Homebrew from https://brew.sh if absent. Full current Xcode is recommended
for distribution and needed to meet the current App Store submission SDK rules.
```sh
xcode-select --install
# Run once if the command-line tools are not installed; let installation finish.
cd /path/to/zoitechat
brew bundle --file packaging/macos/Brewfile
python3 packaging/macos/build.py
open dist/ZoiteChat.app
```
The build compiles the app, runs its Meson tests, stages an installation within
`build-macos/macos-stage`, bundles and rewrites every non-system Mach-O dependency,
creates the icon and plist, audits the result, and signs it ad hoc for local use.
It refuses to overwrite an existing output app. For another build, choose a new
output path, or explicitly delete your old generated app before rebuilding:
```sh
python3 packaging/macos/build.py --output dist/ZoiteChat-test2.app --build-number 2
```
The same build directory can be reconfigured. Use a different build directory
for another architecture or the experimental store variant. Do not change
architectures within one Meson build directory.
Homebrew packages change over time; this is a repeatable procedure, not a
bit-for-bit reproducible dependency lock. The bundle records dependency versions
in `Contents/Resources/homebrew-dependencies.json`. Retain that record and the
release's source commit. For a reproducible release, archive the exact dependency
sources and build them in a controlled prefix, or maintain pinned Homebrew formulae.
## 2. Verify portability and behavior
### Keyboard shortcuts
These defaults apply only to macOS; Linux and Windows shortcuts are unchanged.
| Shortcut | Action |
| --- | --- |
| Command+C / X / V | Copy / cut / paste in text fields, including search and preferences |
| Command+A | Select all text in the focused field |
| Command+Left / Right | Move to the beginning / end of the line |
| Option+Left / Right | Move by words |
| Command+Up / Down | Move to the beginning / end of the text |
| Shift with the movement shortcuts above | Extend the selection |
| Option+Delete (Backspace) | Delete back to the beginning of the word |
| Command+Delete (Backspace) | Delete back to the beginning of the line |
| Command+, | Open Preferences |
| Command+M | Minimize the active window |
| Control+Tab | Completion in the opposite direction; Tab / Shift+Tab still complete normally |
| Command+Option+Left / Right | Move the current tab left / right |
| Command+Shift+R | Reset the chat marker; Command+Shift+M still moves to the marker |
The inherited Control+C/X/V clipboard bindings are removed on macOS only.
Command+C/X/V in multiline editors use plain UTF-8 text directly, avoiding
GTK3 Quartz's incomplete rich-text negotiation and pasteboard types without a
preferred MIME name. Text, newlines, selection replacement and editability
checks are preserved; rich clipboard formatting is not imported by this action.
Command+C copies selected input text first; if no input text is selected, it
copies the selected chat text. Command+X cuts editable text, never messages
from chat history. Password protection and read-only fields retain GTK's
existing behavior. Undo/redo is not added: GTK3's entry widget needs a separate
editing-history implementation before Command+Z can work reliably.
Exact old default assignments for Command+Tab, Option+Left/Right and Command+M
are remapped in memory when loading defaults or an existing configuration.
Other custom bindings are preserved. If a replacement key is already assigned,
both existing assignments are left alone; resolve that conflict in **Settings
→ Keyboard Shortcuts**. Saving there persists the updated assignments.
Command+Tab is left to macOS for switching applications. Plugins and custom
input bindings retain priority; the new native menu accelerators are omitted
when the same key is already assigned to an input action.
Before distributing a build, test the shortcuts in message input, search,
preferences, a detached chat window and a multiline editor, including Unicode,
selection replacement, and read-only/password fields. The Meson widget tests
exercise the GTK actions; the screenshot smoke check does not send real keys
through the macOS event loop.
```sh
python3 -m unittest discover -s packaging/macos/tests -v
python3 packaging/macos/bundle.py audit dist/ZoiteChat.app
python3 packaging/macos/smoke.py dist/ZoiteChat.app
```
The smoke check copies the app to a path containing spaces, verifies its signature,
runs `--version`, and keeps an isolated GUI process alive for ten seconds without
connecting to IRC. It tests startup, not interaction or long-running stability.
### Screenshots and a loopback IRC check without owning a Mac
The ordinary ARM and Intel workflow jobs also run a separate visual check.
After pushing this change to a pull request, open the new **macOS build** run
and download `ZoiteChat-macos-arm64-visual-check` and
`ZoiteChat-macos-x86_64-visual-check` from **Artifacts**. Each contains:
- `zoitechat.png`: the actual running application's window on that architecture.
- `result.json`: the checks completed, window dimensions, architecture and commit.
- `irc.log`: the temporary loopback server's registration, channel and PING/PONG traffic.
- `startup.log`: the application's startup diagnostics.
The check copies the built app to a path containing spaces, verifies its
signature, starts it with temporary settings, connects to a server bound only
to `127.0.0.1`, joins `#ci-smoke`, receives sample text and verifies its PONG
response. It captures the largest visible window owned by that app's process
and rejects failed or nearly uniform captures. No public IRC server or account
is used. The original application artifact is not modified.
This is a supplemental check with `continue-on-error`: a green build alone
does **not** mean the visual check passed. Inspect `result.json` for
`"status": "passed"` and view the PNG. If the runner has no desktop or denies
Screen Recording, the step reports failure and publishes diagnostics instead
of claiming a screenshot. No signing secrets are needed. The check runs in
the ordinary build job; it does not alter the release-signing job.
It can also run on a Mac with a logged-in desktop:
```sh
python3 packaging/macos/visual-smoke.py dist/ZoiteChat.app --output dist/visual-check
```
Use a new output directory for each run. This verifies a narrow set of startup,
IRC and rendering behavior; it does not test keyboard/clipboard interaction,
TLS, notifications, Gatekeeper distribution, or long-running stability.
The audit verifies the entire dependency closure, including GTK image/input
modules, GIO TLS, Enchant providers, OpenSSL providers and ZoiteChat plugins. It
fails on missing libraries, external symlinks/search paths, architecture mismatches
or unresolved load commands. The minimum macOS version in the plist is derived
from **all** bundled binaries, including Homebrew libraries. Lowering that plist
value does not make newer dependency bottles run on an older Mac.
Before releasing, test on a second Mac without Homebrew installed:
- Launch from Finder and from a relocated/read-only location; verify Dock and
menu bar, preferences persistence, dark/light themes and normal shutdown.
- Connect with verified TLS, exercise SASL authentication and reconnect.
- Send Unicode/emoji, render SVG/PNG/JPEG icons/images, and test HTTPS image previews.
- Verify clipboard shortcuts, input methods, notifications/permission denial,
detach/reattach tabs, focus changes, unread highlights and notification clicks.
- Send/receive/resume DCC, write downloads and logs, import/export themes/settings.
- Test FiSH, Lua and DCC checksum plugins in the direct build.
- Exercise accessibility with VoiceOver and keyboard-only navigation.
Direct builds include Lua, FiSH and checksum, and retain the plugin loader.
Python/Perl are explicitly not bundled: reliable redistribution requires packaging
and auditing their interpreter runtimes, standard libraries and extensions.
Linux `/proc`/PCI sysinfo is not built on macOS. These are macOS build choices;
other platforms' plugin defaults are unchanged. macOS uses the existing password
storage fallback; this patch does not add a Keychain backend. Audit credentials
storage before adopting a stronger macOS security claim.
Desktop URL registration (`irc://` in Finder/browser) is not advertised by the
plist; command-line IRC URLs continue to work. A native open-URL event handler
and single-instance forwarding need separate integration before registering those
schemes. AppIndicator and Linux D-Bus scripting/single-instance support are not
used on macOS.
## 3. Sign and notarize for direct downloads
Local ad-hoc builds are for testing; they are not notarized releases. For public
release, enroll in Apple's Developer Program, install your **Developer ID
Application** certificate with its private key, then sign using that identity.
Use your actual certificate identity in place of the example:
```sh
python3 packaging/macos/bundle.py sign dist/ZoiteChat.app \
--identity 'Developer ID Application: YOUR NAME (TEAMID)' \
--entitlements packaging/macos/direct.entitlements
# Store credentials once in your login Keychain; enter your own values interactively.
xcrun notarytool store-credentials ZoiteChat-notary
ditto -c -k --keepParent dist/ZoiteChat.app dist/ZoiteChat-notarization.zip
xcrun notarytool submit dist/ZoiteChat-notarization.zip \
--keychain-profile ZoiteChat-notary --wait
# Only after notarization has succeeded:
xcrun stapler staple dist/ZoiteChat.app
xcrun stapler validate dist/ZoiteChat.app
spctl --assess --type execute --verbose=2 dist/ZoiteChat.app
ditto -c -k --keepParent dist/ZoiteChat.app dist/ZoiteChat-macos.zip
```
Signing runs inside-out, verifies the final bundle and enables hardened runtime.
It does not use `codesign --deep` to sign. `--deep` is used only for verification.
The direct entitlement allows users' independently signed native plugins; it
relaxes library validation, not the entire hardened runtime. No JIT or executable
memory entitlements are added. Do not change bundle contents after signing; signing
again is required after any code/resource edit. Stapling is Apple's supported
post-signing operation.
Publish exact corresponding source for ZoiteChat and satisfy the applicable
licenses of each bundled dependency. Notices and dependency metadata are included,
but those alone do not satisfy every source-distribution obligation. Review the
notarization log if rejected; the script does not silently bypass Gatekeeper.
### Automate signing and notarization with GitHub Actions
The workflow now has a separate signed/notarized release job for both
architectures. It uses Developer ID distribution for direct downloads, not Mac
App Store signing. You can manage it from Windows/Linux; GitHub's macOS runners
perform all signing, notarization and stapling.
Prepare these repository Actions secrets under **Settings → Secrets and
variables → Actions**, or store them as environment secrets in **macos-release**.
The certificate setting names are unchanged from the original instructions.
The three `APPLE_API_*` settings are new notarization credentials:
| Name | Type | Value |
| --- | --- | --- |
| `MACOS_CERTIFICATE_P12_BASE64` | Secret | Base64-encoded Developer ID Application `.p12`, including its private key |
| `MACOS_CERTIFICATE_PASSWORD` | Secret | Password used to export that `.p12` |
| `MACOS_SIGNING_IDENTITY` | Variable, or Secret | Exact `Developer ID Application: YOUR NAME (TEAMID)` identity; the matching certificate's SHA-1 fingerprint is also accepted |
| `APPLE_API_KEY_P8` | Secret | Entire plain-text App Store Connect **Team API key** `.p8` file, including PEM header/footer and newlines |
| `APPLE_API_KEY_ID` | Secret | That API key's Key ID |
| `APPLE_API_ISSUER_ID` | Secret | The Team API key's Issuer ID UUID; this is **not** your Apple Team ID |
Repository variables are under the **Variables** tab. If the identity is entered
as both a variable and a secret, the variable takes precedence. No separate
keychain password is needed; the job generates a temporary random password.
The `.p8` is the notarization API key; it does not replace the signing `.p12`.
No Apple ID password or app-specific password is used by this configuration.
To obtain the API key, open **App Store Connect → Users and Access → Integrations
→ App Store Connect API → Team Keys → Generate API Key**. Choose **Developer**
access for notarization. If API access is not enabled, the Account Holder must
request it first. Download the `.p8` and record the Key ID and Issuer ID. Use a
**Team** key with this workflow: individual API keys use different authentication
and are not supported by this issuer-based configuration. Apple allows the
private key download only once, so retain your copy securely.
For the certificate, export the Developer ID Application identity (certificate
plus private key) from Keychain Access as a password-protected `.p12`.
If you generated the CSR with OpenSSL on Linux/WSL, you can also create it there.
Assuming Apple's downloaded DER certificate has been saved as
`developerID_application.cer`, and you retained `zoitechat-signing.key`:
```sh
openssl x509 -inform DER -in developerID_application.cer \
-out zoitechat-signing.pem
openssl pkcs12 -export -inkey zoitechat-signing.key \
-in zoitechat-signing.pem -out ZoiteChat-signing.p12
# Enter the existing private-key password, then choose the .p12 export password.
# Copy the resulting Base64 text into MACOS_CERTIFICATE_P12_BASE64:
openssl base64 -A -in ZoiteChat-signing.p12 -out ZoiteChat-signing.p12.base64
# Read the certificate's commonName to obtain MACOS_SIGNING_IDENTITY:
openssl x509 -in zoitechat-signing.pem -noout -subject -nameopt multiline
```
Upload only the public CSR to Apple. Put the `.p12` Base64, export password and
API credentials in GitHub settings, never in the repository or build artifacts.
Once these patches are committed to `master` and the settings are ready:
1. Open **Actions → macOS build → Run workflow**.
2. Select **master** and enable **Sign with Developer ID and notarize using
release credentials**.
3. Run the workflow. It creates separate Apple Silicon and Intel release jobs.
4. After success, download `ZoiteChat-macos-arm64-signed-notarized` or
`ZoiteChat-macos-x86_64-signed-notarized`. The ZIP inside contains the signed,
stapled app. Actions artifacts wrap it in their own download archive.
A push of a `v*` tag also triggers the signed release jobs automatically, so wait
until credentials are configured before pushing a release tag. Normal branch
pushes, pull requests and manual runs with the checkbox off continue to build
ad-hoc test artifacts without any signing secrets. Manual credentialed releases
are restricted to `master`; pull requests cannot invoke the release job.
Missing/malformed release settings fail before the Homebrew/build steps rather
than producing an unsigned artifact marked as a release.
The release jobs use the GitHub environment **macos-release**. Repository secrets
work with it; environment secrets can override them. Configure environment
branch/tag restrictions or required reviewers if that fits your repository's
release process. This workflow does not automatically create a GitHub Release,
publish to zoitechat.org, or upload to App Store Connect; it produces downloadable
signed/notarized artifacts for you to publish.
The job imports the `.p12` into a temporary keychain, installs Apple's public
Developer ID intermediate certificates, verifies a valid matching identity,
and signs inside-out with hardened runtime and the direct plugin entitlement.
It uses the existing bundle audit and then submits a ZIP with `notarytool`.
After Apple reports **Accepted**, it staples the ticket to the `.app`, verifies
stapling/signatures and Gatekeeper acceptance, and creates a **new** final ZIP.
Private credential files and the temporary keychain are cleaned up on normal
success/failure paths; hosted runner VMs are also discarded after the job.
The default Apple wait limit is **60 minutes** within a **180-minute** release
job. A rejected, failed or still-pending submission produces no final release
ZIP. The `ZoiteChat-macos-<arch>-notarization-log` artifact retains the submission
ID, status and Apple's available diagnostic log. First submissions can take
longer; check the saved ID with `notarytool info` and your API credentials. An
accepted submission still requires stapling and repackaging before publication;
rerunning this workflow starts a new build and submission. To increase the wait
limit, update `--notary-timeout` and allow adequate job timeout for build plus wait.
Both architecture builds receive the same bundle build number for one workflow
run. The API credentials are supplied only to the config-check/release steps,
not to Homebrew, compiler tests, ordinary builds or pull-request jobs.
## 4. Mac App Store: experimental groundwork, not submission-ready
A working direct-download app is not sufficient for App Store submission. This
repository inherits GPL-2.0-or-later code from XChat/HexChat and other contributors.
App Store terms and GPL obligations require a licensing review; do not assume
that signing, a custom EULA, or relicensing only your new changes resolves rights
to inherited code. This patch changes no licenses or copyright permissions.
An isolated sandbox-test build is provided:
```sh
python3 packaging/macos/build.py --store-experimental \
--build-dir build-macos-store --output dist/ZoiteChat-store-test.app \
--build-number 1
# Local sandbox experimentation (not an uploadable distribution signature):
python3 packaging/macos/bundle.py sign dist/ZoiteChat-store-test.app \
--identity - --entitlements packaging/macos/store-experimental.entitlements
open dist/ZoiteChat-store-test.app
```
The separate store variant excludes loadable plugins and scripting runtimes.
The template grants sandboxed client/server networking, user-selected files and
Downloads. The direct version retains its plugin functionality. This is an
explicit experimental variant, not a change to Windows/Linux or direct builds.
Before a store package can be regarded as a release candidate:
1. Resolve the inherited GPL/dependency-license distribution question and review
Apple's current SDK, content/chat moderation, privacy and accessibility rules.
2. Register `org.Zoitechat` (or change the plist identifier consistently if already
owned by another developer), create App Store Connect metadata, and choose a
unique incrementing build number. Use a stable release, not a `-dev` snapshot.
3. Test sandboxed settings/logs, notifications, SASL/TLS, HTTPS and DCC on a real
Mac. GTK dialogs are not assumed to grant Powerbox access. Add native
`NSOpenPanel`/`NSSavePanel` integration and security-scoped bookmarks for
persistent access to user-selected files. The entitlement template alone does
not solve persistent file access. `/EXEC`, custom external commands, user
plugin loading and arbitrary external paths cannot be promised under sandbox.
4. Add required privacy manifest declarations based on an actual API audit of the
app and dependencies; do not submit an invented empty compliance declaration.
5. Obtain matching App Store distribution application/installer certificates and
a matching provisioning profile. Generate team-specific signing entitlements
from the profile with Xcode; do not use Developer ID or the generic local-test
template as a store distribution identity/profile.
6. Embed the matching profile as `Contents/embedded.provisionprofile` **before**
final signing. Sign embedded libraries with the distribution identity, then
the app with its profile-authorized entitlements (use `bundle.py sign` with
that identity and generated entitlement file). Package with Apple's
`productbuild`, validate and upload with Transporter/App Store Connect, then
test the App Store processed build via TestFlight before submission.
Example **after those prerequisites are satisfied**:
```sh
# Use the actual prepared profile and team-specific entitlements:
cp /path/to/ZoiteChat.provisionprofile \
dist/ZoiteChat-store-test.app/Contents/embedded.provisionprofile
python3 packaging/macos/bundle.py sign dist/ZoiteChat-store-test.app \
--identity 'YOUR APP STORE APPLICATION CERTIFICATE IDENTITY' \
--entitlements /path/to/ZoiteChat-distribution.entitlements
productbuild --component dist/ZoiteChat-store-test.app /Applications \
--sign 'YOUR APP STORE INSTALLER CERTIFICATE IDENTITY' dist/ZoiteChat-store.pkg
```
No App Store approval, sandbox compatibility or legal compatibility is asserted.
For an initial macOS release, the signed/notarized direct build is the practical
path while those issues are resolved.
## 5. CI and architecture support
`.github/workflows/macos-build.yml` adds independent Apple Silicon and Intel jobs.
Ordinary jobs install prerequisites, run tests, create/audit/ad-hoc-sign the
bundle, check relocated startup and upload test ZIPs. They need no Apple secrets.
The separate release jobs add Developer ID signing, Apple notarization, stapling
and final verification. Only their successful output is named signed/notarized.
Homebrew may build unsupported Intel bottles from source; that can be slower.
The workflow uses native runners and does not claim a universal application.
Do not merge only the main executable with `lipo`: a universal release must merge
and validate every bundled dylib, module and plugin plus matching resources.
Signing must happen after that merge. Publish separate architecture downloads
until a full universal dependency pipeline is available.
## References
- GTK on macOS: https://www.gtk.org/docs/installations/macos
- GTK macOS backend: https://docs.gtk.org/gtk3/macos.html
- Apple distribution signing: https://developer.apple.com/documentation/xcode/creating-distribution-signed-code-for-the-mac
- Apple packaging: https://developer.apple.com/documentation/xcode/packaging-mac-software-for-distribution
- GitHub certificate import: https://docs.github.com/en/actions/how-tos/deploy/deploy-to-third-party-platforms/sign-xcode-applications
- Apple notarization authentication: https://developer.apple.com/documentation/technotes/tn3147-migrating-to-the-latest-notarization-tool
- App Store Connect API keys: https://developer.apple.com/help/app-store-connect/get-started/app-store-connect-api/
- Apple review rules (especially 1.2 and 2.4.5): https://developer.apple.com/app-store/review/guidelines/
- Apple sandbox file access: https://developer.apple.com/documentation/security/accessing-files-from-the-macos-app-sandbox
- FSF discussion of GPL and App Store terms (historical, not a current legal determination): https://www.fsf.org/blogs/licensing/more-about-the-app-store-gpl-enforcement
+72
View File
@@ -0,0 +1,72 @@
#!/usr/bin/env python3
"""Compile, test, stage and package ZoiteChat on the host Mac."""
from __future__ import annotations
import argparse
import os
from pathlib import Path
import platform
import shutil
import subprocess
import sys
from bundle import ROOT, package, run
def main() -> None:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--build-dir", type=Path, default=Path("build-macos"))
parser.add_argument("--output", type=Path, default=Path("dist/ZoiteChat.app"))
parser.add_argument("--build-number", default="1")
parser.add_argument("--store-experimental", action="store_true", help="Build a separate sandbox test variant without loadable plugins; not submission approval")
args = parser.parse_args()
if platform.system() != "Darwin":
parser.error("Build on macOS; cross-compiling from Linux is not supported")
if args.output.exists():
parser.error("Output already exists; use a new --output or explicitly remove the old app")
build = args.build_dir.resolve()
if build == ROOT or ROOT.is_relative_to(build):
parser.error("Build directory must not contain the source checkout")
run("xcrun", "--find", "clang")
env = os.environ.copy()
prefixes = [Path(run("brew", "--prefix", name)) for name in ("gettext", "openssl@3", "libarchive", "lua", "gtk+3", "gtk-mac-integration")]
brew = Path(run("brew", "--prefix"))
# Keg-only gettext, OpenSSL and libarchive are not necessarily in PATH/pkg-config.
env["PATH"] = os.pathsep.join([str(p / "bin") for p in prefixes] + [str(brew / "bin"), env.get("PATH", "")])
env["PKG_CONFIG_PATH"] = os.pathsep.join([str(p / "lib/pkgconfig") for p in prefixes] + [str(brew / "lib/pkgconfig"), str(brew / "share/pkgconfig"), env.get("PKG_CONFIG_PATH", "")])
# Native dependency bottles determine the true deployment target; the bundle
# auditor derives the final minimum rather than promising an arbitrary old OS.
env.setdefault("MACOSX_DEPLOYMENT_TARGET", "11.0")
backend = run("pkg-config", "--variable=targets", "gtk+-3.0", env=env).split()
if "quartz" not in backend:
parser.error(f"GTK must provide the native Quartz backend, got {backend}")
# Do not silently select an XQuartz GTK stack or mix Intel/ARM Homebrew prefixes.
machine = platform.machine()
if machine not in ("arm64", "x86_64"):
parser.error(f"Unsupported architecture: {machine}")
options = [
"--prefix=/", "--libdir=lib", "--buildtype=release", "-Dgtk-frontend=true",
"-Dtext-frontend=false", "-Dmac-integration=enabled", "-Ddbus=disabled",
"-Dlibcanberra=disabled", "-Dappindicator=disabled", "-Dinstall-appdata=false",
"-Dinstall-docs=false",
"-Dwith-sysinfo=false", "-Dwith-python=false", "-Dwith-perl=false",
"-Dwith-lua=lua", "-Dwith-fishlim=true", "-Dwith-checksum=true",
"-Dplugin=" + ("false" if args.store_experimental else "true"),
]
if (build / "meson-private/coredata.dat").exists():
options.insert(0, "--reconfigure")
subprocess.run(["meson", "setup", str(build), str(ROOT), *options], env=env, check=True)
subprocess.run(["meson", "compile", "-C", str(build)], env=env, check=True)
subprocess.run(["meson", "test", "-C", str(build), "--print-errorlogs"], env=env, check=True)
stage = build / "macos-stage"
if stage.exists():
shutil.rmtree(stage)
subprocess.run(["meson", "install", "-C", str(build), "--destdir", str(stage)], env=env, check=True)
package(stage, args.output.resolve(), args.build_number)
if __name__ == "__main__":
try:
main()
except (RuntimeError, subprocess.CalledProcessError, OSError) as error:
sys.exit(f"macOS build failed: {error}")
+374
View File
@@ -0,0 +1,374 @@
#!/usr/bin/env python3
"""Build and audit a relocatable GTK3 application. No third-party Python packages."""
from __future__ import annotations
import argparse
import json
import os
from pathlib import Path
import platform
import plistlib
import re
import shutil
import subprocess
import sys
import xml.etree.ElementTree as ET
ROOT = Path(__file__).resolve().parents[2]
MACH_MAGIC = {bytes.fromhex(h) for h in ("feedface", "cefaedfe", "feedfacf", "cffaedfe", "cafebabe", "bebafeca", "cafebabf", "bfbafeca")}
SYSTEM_ROOTS = ("/usr/lib/", "/System/Library/")
def run(*args: str | Path, env: dict[str, str] | None = None) -> str:
return subprocess.check_output([str(arg) for arg in args], env=env, text=True).strip()
def is_macho(path: Path) -> bool:
if not path.is_file() or path.is_symlink():
return False
with path.open("rb") as stream:
return stream.read(4) in MACH_MAGIC
def dependencies(path: Path) -> list[str]:
# Paths may contain spaces; splitting on whitespace would corrupt them.
return re.findall(r"^\s+(.+?) \(compatibility version", run("otool", "-L", path), re.M)
def rpaths(path: Path) -> list[str]:
return re.findall(r"cmd LC_RPATH\s+cmdsize \d+\s+path (.+?) \(offset", run("otool", "-l", path))
def dylib_id(path: Path) -> str | None:
lines = run("otool", "-D", path).splitlines()
return lines[1].strip() if len(lines) > 1 else None
def system_library(name: str) -> bool:
return name.startswith(SYSTEM_ROOTS)
def expand_path(name: str, loader: Path, executable: Path) -> Path:
return Path(name.replace("@loader_path", str(loader.parent)).replace("@executable_path", str(executable.parent)))
def resolve_dependency(name: str, loader: Path, executable: Path, search: list[Path]) -> Path:
if name.startswith("@rpath/"):
suffix = name[len("@rpath/"):]
candidates = [directory / suffix for directory in search]
else:
candidates = [expand_path(name, loader, executable)]
for candidate in candidates:
if candidate.is_absolute() and candidate.is_file():
return candidate.resolve()
raise RuntimeError(f"Cannot resolve {name!r} from {loader}; searched {candidates}")
class Relocator:
def __init__(self, app: Path, executable: Path):
self.app = app
self.executable = executable
self.frameworks = app / "Contents/Frameworks"
self.frameworks.mkdir(parents=True)
self.sources: dict[Path, Path] = {}
self.names: dict[str, Path] = {}
self.visited: set[Path] = set()
def copy_dependency(self, source: Path) -> Path:
source = source.resolve()
if ".framework/" in str(source):
raise RuntimeError(f"Third-party frameworks require separate packaging support: {source}")
if source in self.sources:
return self.sources[source]
existing = self.names.get(source.name)
if existing and existing != source:
raise RuntimeError(f"Conflicting library basename: {existing} and {source}")
target = self.frameworks / source.name
shutil.copy2(source, target)
target.chmod(target.stat().st_mode | 0o200)
self.sources[source] = target
self.names[source.name] = source
return target
def relocate(self, source: Path, target: Path, inherited: list[Path] | None = None) -> None:
source = source.resolve()
if target in self.visited:
return
self.visited.add(target)
local = [expand_path(name, source, self.executable) for name in rpaths(source)]
search = local + (inherited or [])
identity = dylib_id(source)
# Keep the existing signature allocation while editing: stripping it
# first can leave __LINKEDIT padding that install_name_tool rejects.
# The final inside-out codesign --force pass replaces stale signatures.
edits: list[str | Path] = []
for name in dependencies(source):
if name == identity or system_library(name):
continue
dependency = resolve_dependency(name, source, self.executable, search)
destination = self.copy_dependency(dependency)
relative = os.path.relpath(destination, target.parent)
edits += ["-change", name, "@loader_path/" + relative]
self.relocate(dependency, destination, search)
if identity:
edits += ["-id", "@rpath/" + target.name]
for name in sorted(set(rpaths(target))):
edits += ["-delete_rpath", name]
if edits:
run("install_name_tool", *edits, target)
def copy_tree(source: Path, target: Path) -> None:
if source.is_dir():
# Dereference symlinks: never leave links into the builder's Cellar.
shutil.copytree(source, target, dirs_exist_ok=True, symlinks=False)
def write_fontconfig(resources: Path, fontconfig_prefix: Path) -> None:
# Noto Color Emoji has fixed-size color bitmaps. Fontconfig supplies the
# requested-size/strike-size matrix; without it glyphs render at 109px.
rule = fontconfig_prefix / "share/fontconfig/conf.avail/10-scale-bitmap-fonts.conf"
if not rule.is_file():
raise RuntimeError(f"Required Fontconfig bitmap scaling rules not found: {rule}")
resources.mkdir(parents=True, exist_ok=True)
config = ET.fromstring('''<fontconfig>
<dir>/System/Library/Fonts</dir><dir>/Library/Fonts</dir><dir>~/Library/Fonts</dir>
<cachedir prefix="xdg">fontconfig</cachedir>
<alias><family>sans-serif</family><prefer><family>Helvetica</family></prefer></alias>
<alias><family>monospace</family><prefer><family>Menlo</family></prefer></alias>
</fontconfig>
''')
# Embed the standard rules so they work after relocation without depending
# on Homebrew or Fontconfig's host configuration search paths.
for element in reversed(list(ET.parse(rule).getroot())):
config.insert(0, element)
(resources / "fonts.conf").write_text(
'<?xml version="1.0"?>\n<!DOCTYPE fontconfig SYSTEM "urn:fontconfig:fonts.dtd">\n' +
ET.tostring(config, encoding="unicode") + "\n", encoding="utf-8")
def audit(app: Path) -> dict[str, object]:
binary = app / "Contents/MacOS/zoitechat"
architectures = set(run("lipo", "-archs", binary).split())
minimum = (11, 0, 0)
entries = []
for path in sorted(app.rglob("*")):
if path.is_symlink():
resolved = path.resolve()
if not resolved.is_relative_to(app.resolve()):
raise RuntimeError(f"External symlink: {path} -> {resolved}")
if not is_macho(path):
continue
if not architectures.issubset(set(run("lipo", "-archs", path).split())):
raise RuntimeError(f"Architecture mismatch: {path}")
identity = dylib_id(path)
for name in dependencies(path):
if name == identity or system_library(name):
continue
if not name.startswith("@loader_path/"):
raise RuntimeError(f"Non-relocatable dependency: {path}: {name}")
target = expand_path(name, path, binary).resolve()
if not target.is_relative_to(app.resolve()) or not target.is_file():
raise RuntimeError(f"Missing or external dependency: {path}: {name}")
if rpaths(path):
raise RuntimeError(f"Unexpected runtime search paths: {path}")
load_commands = run("otool", "-l", path)
versions = re.findall(r"\bminos (\d+(?:\.\d+){0,2})", load_commands)
versions += re.findall(r"cmd LC_VERSION_MIN_MACOSX\s+cmdsize \d+\s+version (\d+(?:\.\d+){0,2})", load_commands)
if not versions:
raise RuntimeError(f"Missing deployment version: {path}")
for version in versions:
parts = tuple(int(value) for value in version.split("."))
minimum = max(minimum, parts + (0,) * (3 - len(parts)))
entries.append({"path": str(path.relative_to(app))})
return {"architectures": sorted(architectures), "minimum_macos": ".".join(map(str, minimum if minimum[2] else minimum[:2])), "mach_o": entries}
def package(stage: Path, app: Path, build_number: str) -> None:
if not re.fullmatch(r"[1-9]\d*(?:\.\d+){0,2}", build_number):
raise RuntimeError("Build number must be a positive Apple CFBundleVersion (for example 1 or 12.3)")
if app.exists():
raise RuntimeError(f"Output already exists; choose another --output or remove it explicitly: {app}")
app.mkdir(parents=True)
resources = app / "Contents/Resources"
resources.mkdir(parents=True)
binary = app / "Contents/MacOS/zoitechat"
binary.parent.mkdir()
shutil.copy2(stage / "bin/zoitechat", binary)
copy_tree(stage / "share", resources / "share")
copy_tree(stage / "lib/zoitechat/plugins", resources / "lib/zoitechat/plugins")
relocator = Relocator(app, stage / "bin/zoitechat")
seeds: list[tuple[Path, Path]] = [(stage / "bin/zoitechat", binary)]
for source in (stage / "lib/zoitechat/plugins").glob("*"):
if is_macho(source):
seeds.append((source, resources / "lib/zoitechat/plugins" / source.name))
prefixes = {formula: Path(run("brew", "--prefix", formula)) for formula in (
"gtk+3", "gdk-pixbuf", "glib", "glib-networking", "librsvg", "enchant", "adwaita-icon-theme", "hicolor-icon-theme", "gsettings-desktop-schemas", "ca-certificates", "openssl@3", "lua", "libarchive", "gtk-mac-integration", "fontconfig")}
# Runtime-loaded code is not visible in the main executable's otool output.
module_groups = [
(prefixes["gdk-pixbuf"] / "lib/gdk-pixbuf-2.0/2.10.0/loaders", "lib/gdk-pixbuf-2.0/2.10.0/loaders"),
(prefixes["librsvg"] / "lib/gdk-pixbuf-2.0/2.10.0/loaders", "lib/gdk-pixbuf-2.0/2.10.0/loaders"),
(prefixes["gtk+3"] / "lib/gtk-3.0/3.0.0/immodules", "lib/gtk-3.0/3.0.0/immodules"),
(prefixes["glib-networking"] / "lib/gio/modules", "lib/gio/modules"),
(prefixes["enchant"] / "lib/enchant-2", "lib/enchant-2"),
(prefixes["openssl@3"] / "lib/ossl-modules", "lib/ossl-modules"),
]
for source_dir, relative in module_groups:
target_dir = resources / relative
target_dir.mkdir(parents=True, exist_ok=True)
for source in sorted(source_dir.glob("*")):
if source.suffix not in (".so", ".dylib") or not source.is_file():
continue
target = target_dir / source.name
if target.exists() and target.read_bytes() != source.read_bytes():
raise RuntimeError(f"Conflicting module: {target}")
shutil.copy2(source.resolve(), target)
seeds.append((source.resolve(), target))
enchant = (prefixes["enchant"] / "lib/libenchant-2.dylib").resolve()
# Register Enchant at Resources/lib so its public prefix relocation API
# finds Resources/lib/enchant-2 and Resources/share/enchant.
enchant_alias = resources / "lib/libenchant-2.dylib"
shutil.copy2(enchant, enchant_alias)
relocator.sources[enchant] = enchant_alias
relocator.names[enchant.name] = enchant
seeds.append((enchant, enchant_alias))
for formula, prefix in prefixes.items():
for name in ("icons", "glib-2.0/schemas", "locale", "enchant", "hunspell"):
copy_tree(prefix / "share" / name, resources / "share" / name)
ca = prefixes["ca-certificates"] / "share/ca-certificates/cacert.pem"
if not ca.is_file():
raise RuntimeError(f"CA bundle not found: {ca}")
(resources / "certs").mkdir()
shutil.copy2(ca, resources / "certs/ca-bundle.crt")
schemas = resources / "share/glib-2.0/schemas"
run(prefixes["glib"] / "bin/glib-compile-schemas", schemas)
pixbuf_modules = sorted((resources / "lib/gdk-pixbuf-2.0/2.10.0/loaders").glob("*"))
if not any("svg" in path.name for path in pixbuf_modules):
raise RuntimeError("SVG loader missing: install librsvg")
if list((resources / "lib/zoitechat/plugins").glob("fishlim.*")) and not list((resources / "lib/ossl-modules").glob("legacy.*")):
raise RuntimeError("FiSH needs the OpenSSL legacy provider in the bundle")
if not list((resources / "lib/gio/modules").glob("*tls*")):
raise RuntimeError("GIO TLS backend missing: install glib-networking")
# Query original binaries before relocation, then rewrite only the cached paths.
pixbuf_query = prefixes["gdk-pixbuf"] / "bin/gdk-pixbuf-query-loaders"
if not pixbuf_query.exists():
pixbuf_query = prefixes["gdk-pixbuf"] / "lib/gdk-pixbuf-2.0/gdk-pixbuf-query-loaders"
for tool, module_dir, cache in (
(pixbuf_query, "lib/gdk-pixbuf-2.0/2.10.0/loaders", "pixbuf-loaders.cache"),
(prefixes["gtk+3"] / "bin/gtk-query-immodules-3.0", "lib/gtk-3.0/3.0.0/immodules", "gtk-immodules.cache"),
):
originals = [(source, target) for source, target in seeds if target.parent == resources / module_dir]
# Never call a query tool with zero arguments: it would scan global modules.
data = run(tool, *(source for source, _ in originals)) if originals else "# No external input-method modules\n"
for source, target in originals:
data = data.replace(str(source), "@ZOITECHAT_RESOURCES@/" + str(target.relative_to(resources)))
(resources / cache).write_text(data + "\n")
# The private font map loads the bundled Noto font; ordinary GTK fonts stay local.
write_fontconfig(resources, prefixes["fontconfig"])
(resources / "zoitechat-bundle").write_text("1\n")
licenses = resources / "licenses"
licenses.mkdir()
shutil.copy2(ROOT / "COPYING", licenses / "ZoiteChat-COPYING")
# Preserve the notices shipped with every installed Homebrew dependency.
# brew deps traverses dependencies by default; --recursive is not an option.
wanted = set(prefixes)
for formula in prefixes:
wanted.update(run("brew", "deps", "--formula", formula).splitlines())
installed = [item for item in json.loads(run("brew", "info", "--json=v2", "--installed"))["formulae"]
if item["name"] in wanted]
for formula in installed:
prefix = Path(run("brew", "--prefix", formula["name"]))
notices = [p for p in prefix.iterdir() if p.is_file() and re.match(r"(?i)(copying|license|copyright|notice)", p.name)]
if notices:
target = licenses / formula["name"].replace("/", "-")
target.mkdir(exist_ok=True)
for notice in notices:
shutil.copy2(notice, target / notice.name)
(resources / "homebrew-dependencies.json").write_text(json.dumps(installed, indent=2) + "\n")
for source, target in seeds:
relocator.relocate(source, target)
report = audit(app)
(resources / "bundle-audit.json").write_text(json.dumps(report, indent=2) + "\n")
# Use the actual highest deployment target of the executable and all dylibs.
# Setting an older plist version cannot make newer Homebrew bottles compatible.
version = (ROOT / "VERSION").read_text().strip()
release = re.match(r"\d+\.\d+\.\d+", version)
if not release:
raise RuntimeError(f"Unsupported release version: {version}")
plist = {
"CFBundleIdentifier": "org.Zoitechat", "CFBundleExecutable": "zoitechat",
"CFBundleName": "ZoiteChat", "CFBundleDisplayName": "ZoiteChat",
"CFBundlePackageType": "APPL", "CFBundleShortVersionString": release.group(),
"CFBundleVersion": build_number, "CFBundleIconFile": "ZoiteChat.icns",
"LSMinimumSystemVersion": report["minimum_macos"], "NSHighResolutionCapable": True,
"NSPrincipalClass": "NSApplication",
"LSApplicationCategoryType": "public.app-category.social-networking",
"DTSDKName": "macosx" + run("xcrun", "--sdk", "macosx", "--show-sdk-version"),
"DTSDKBuild": run("xcrun", "--sdk", "macosx", "--show-sdk-build-version"),
}
with (app / "Contents/Info.plist").open("wb") as stream:
plistlib.dump(plist, stream)
iconset = app.parent / (app.stem + ".iconset")
if iconset.exists():
raise RuntimeError(f"Icon staging directory already exists: {iconset}")
iconset.mkdir()
try:
# Rasterize the original vector at 1024px, then generate every icon size.
large = iconset / "source.png"
run(prefixes["librsvg"] / "bin/rsvg-convert", "-w", "1024", "-h", "1024", "-o", large, ROOT / "data/icons/zoitechat.svg")
for size in (16, 32, 128, 256, 512):
for scale in (1, 2):
name = f"icon_{size}x{size}" + ("@2x" if scale == 2 else "") + ".png"
run("sips", "-z", str(size * scale), str(size * scale), large, "--out", iconset / name)
large.unlink()
run("iconutil", "-c", "icns", iconset, "-o", resources / "ZoiteChat.icns")
finally:
shutil.rmtree(iconset)
# Local builds need an ad-hoc signature after install_name_tool changes.
sign(app, "-", None)
print(f"Created {app}: {', '.join(report['architectures'])}, macOS {report['minimum_macos']}+")
def sign(app: Path, identity: str, entitlements: Path | None, *, keychain: Path | None = None) -> None:
audit(app)
binaries = [p for p in app.rglob("*") if is_macho(p) and p != app / "Contents/MacOS/zoitechat"]
options = [] if identity == "-" else ["--timestamp", "--options", "runtime"]
if keychain is not None:
options += ["--keychain", str(keychain)]
for binary in sorted(binaries, key=lambda p: len(p.parts), reverse=True):
run("codesign", "--force", "--sign", identity, *options, binary)
main_options = ["--entitlements", str(entitlements)] if entitlements else []
run("codesign", "--force", "--sign", identity, *options, *main_options, app)
run("codesign", "--verify", "--deep", "--strict", "--verbose=2", app)
def main() -> None:
parser = argparse.ArgumentParser(description=__doc__)
sub = parser.add_subparsers(dest="action", required=True)
create = sub.add_parser("create")
create.add_argument("--stage", type=Path, required=True)
create.add_argument("--app", type=Path, required=True)
create.add_argument("--build-number", default="1")
verify = sub.add_parser("audit")
verify.add_argument("app", type=Path)
signing = sub.add_parser("sign")
signing.add_argument("app", type=Path)
signing.add_argument("--identity", required=True)
signing.add_argument("--entitlements", type=Path)
args = parser.parse_args()
if platform.system() != "Darwin":
parser.error("This tool requires macOS and the Xcode command-line tools")
if args.action == "create":
package(args.stage.resolve(), args.app.resolve(), args.build_number)
elif args.action == "audit":
print(json.dumps(audit(args.app.resolve()), indent=2))
else:
sign(args.app.resolve(), args.identity, args.entitlements)
if __name__ == "__main__":
try:
main()
except (RuntimeError, subprocess.CalledProcessError, OSError) as error:
sys.exit(f"macOS packaging failed: {error}")
+72
View File
@@ -0,0 +1,72 @@
// Capture only the tested process's window; a desktop-only image is not evidence.
import AppKit
import CoreGraphics
import Foundation
func fail(_ message: String) -> Never {
FileHandle.standardError.write(Data((message + "\n").utf8))
exit(1)
}
guard CommandLine.arguments.count == 3,
let pid = Int32(CommandLine.arguments[1]) else {
fail("Usage: capture-window PID OUTPUT.png")
}
let output = URL(fileURLWithPath: CommandLine.arguments[2])
let deadline = Date().addingTimeInterval(30)
var chosen: [String: Any]?
while Date() < deadline {
guard let app = NSRunningApplication(processIdentifier: pid), !app.isTerminated else {
fail("ZoiteChat is not running")
}
app.activate(options: [])
RunLoop.current.run(until: Date().addingTimeInterval(0.2))
let windows = CGWindowListCopyWindowInfo([.optionOnScreenOnly, .excludeDesktopElements],
kCGNullWindowID) as? [[String: Any]] ?? []
let owned = windows.filter { window in
guard (window[kCGWindowOwnerPID as String] as? NSNumber)?.int32Value == pid,
(window[kCGWindowLayer as String] as? NSNumber)?.intValue == 0,
let bounds = window[kCGWindowBounds as String] as? [String: Any],
let width = bounds["Width"] as? NSNumber,
let height = bounds["Height"] as? NSNumber else { return false }
return width.doubleValue >= 600 && height.doubleValue >= 300
}
chosen = owned.max { left, right in
func area(_ window: [String: Any]) -> Double {
let bounds = window[kCGWindowBounds as String] as? [String: NSNumber] ?? [:]
return (bounds["Width"]?.doubleValue ?? 0) * (bounds["Height"]?.doubleValue ?? 0)
}
return area(left) < area(right)
}
if chosen != nil { break }
}
guard let window = chosen,
let identifier = window[kCGWindowNumber as String] as? NSNumber else {
fail("No visible ZoiteChat window found in this runner's desktop session")
}
let capture = Process()
capture.executableURL = URL(fileURLWithPath: "/usr/sbin/screencapture")
capture.arguments = ["-x", "-o", "-t", "png", "-l", identifier.stringValue, output.path]
do { try capture.run() } catch { fail("Cannot start screencapture: \(error)") }
capture.waitUntilExit()
guard capture.terminationStatus == 0,
let png = try? Data(contentsOf: output),
let bitmap = NSBitmapImageRep(data: png),
bitmap.pixelsWide >= 600, bitmap.pixelsHigh >= 300 else {
fail("Application window capture failed; check runner Screen Recording permissions")
}
var colors = Set<String>()
for y in stride(from: 0, to: bitmap.pixelsHigh, by: max(1, bitmap.pixelsHigh / 60)) {
for x in stride(from: 0, to: bitmap.pixelsWide, by: max(1, bitmap.pixelsWide / 80)) {
if let color = bitmap.colorAt(x: x, y: y)?.usingColorSpace(.deviceRGB) {
colors.insert("\(Int(color.redComponent * 255)),\(Int(color.greenComponent * 255)),\(Int(color.blueComponent * 255))")
}
}
}
guard colors.count >= 8 else { fail("Window capture is blank or nearly uniform") }
let evidence: [String: Any] = ["pid": pid, "window_id": identifier.intValue,
"width": bitmap.pixelsWide, "height": bitmap.pixelsHigh, "sampled_colors": colors.count]
do {
let data = try JSONSerialization.data(withJSONObject: evidence, options: [.sortedKeys])
print(String(decoding: data, as: UTF8.self))
} catch { fail("Cannot serialize screenshot evidence: \(error)") }
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"><dict>
<!-- User-installed native plugins need library validation disabled. -->
<key>com.apple.security.cs.disable-library-validation</key><true/>
</dict></plist>
+220
View File
@@ -0,0 +1,220 @@
#!/usr/bin/env python3
"""Developer ID sign, notarize and staple a built app using CI environment secrets."""
from __future__ import annotations
import argparse
import base64
import binascii
from contextlib import contextmanager
from dataclasses import dataclass
import json
import os
from pathlib import Path
import platform
import re
import secrets
import shlex
import subprocess
import sys
import tempfile
import urllib.request
import uuid
from bundle import sign
REQUIRED = (
"MACOS_CERTIFICATE_P12_BASE64", "MACOS_CERTIFICATE_PASSWORD",
"MACOS_SIGNING_IDENTITY", "APPLE_API_KEY_P8", "APPLE_API_KEY_ID", "APPLE_API_ISSUER_ID",
)
@dataclass(repr=False)
class Credentials:
certificate: bytes
password: str
identity: str
api_key: str
key_id: str
issuer: str
@classmethod
def from_environment(cls) -> Credentials:
missing = [name for name in REQUIRED if not os.environ.get(name, "").strip()]
if missing:
raise RuntimeError("Missing release settings: " + ", ".join(missing))
try:
certificate = base64.b64decode("".join(os.environ[REQUIRED[0]].split()), validate=True)
except (binascii.Error, ValueError):
raise RuntimeError("MACOS_CERTIFICATE_P12_BASE64 must contain Base64-encoded .p12 bytes") from None
if not certificate:
raise RuntimeError("Signing certificate is empty")
identity = os.environ["MACOS_SIGNING_IDENTITY"].strip()
if not (identity.startswith("Developer ID Application:") or re.fullmatch(r"[a-fA-F0-9]{40}", identity)):
raise RuntimeError("MACOS_SIGNING_IDENTITY must identify a Developer ID Application certificate")
api_key = os.environ["APPLE_API_KEY_P8"].strip()
if not (api_key.startswith("-----BEGIN PRIVATE KEY-----") and api_key.endswith("-----END PRIVATE KEY-----")):
raise RuntimeError("APPLE_API_KEY_P8 must contain the complete, plain-text .p8 private key")
key_id = os.environ["APPLE_API_KEY_ID"].strip()
if not re.fullmatch(r"[A-Za-z0-9]{6,32}", key_id):
raise RuntimeError("APPLE_API_KEY_ID must be an App Store Connect key ID")
try:
issuer = str(uuid.UUID(os.environ["APPLE_API_ISSUER_ID"].strip()))
except ValueError:
raise RuntimeError("APPLE_API_ISSUER_ID must be the Team API key issuer UUID") from None
return cls(certificate, os.environ["MACOS_CERTIFICATE_PASSWORD"], identity, api_key, key_id, issuer)
def execute(args: list[str | Path], operation: str, *, check: bool = True) -> subprocess.CompletedProcess[str]:
# Do not print argv or CalledProcessError: security commands contain passwords.
try:
result = subprocess.run([str(arg) for arg in args], capture_output=True, text=True, check=False)
except OSError:
raise RuntimeError(f"{operation}: could not start the required tool") from None
if check and result.returncode != 0:
raise RuntimeError(f"{operation} failed (exit {result.returncode})")
return result
def write_private(path: Path, data: bytes) -> None:
with path.open("xb") as stream:
path.chmod(0o600)
stream.write(data)
def resolve_identity(output: str, requested: str) -> str:
for fingerprint, name in re.findall(r'\b([A-Fa-f0-9]{40})\s+"([^"\n]+)"', output):
if name.startswith("Developer ID Application:") and (requested == name or requested.upper() == fingerprint.upper()):
return fingerprint
raise RuntimeError("Imported .p12 has no valid Developer ID Application identity matching MACOS_SIGNING_IDENTITY; check its private key, password, expiry and certificate chain")
@contextmanager
def signing_keychain(credentials: Credentials):
original = shlex.split(execute(["security", "list-keychains", "-d", "user"], "Read keychain search list").stdout)
with tempfile.TemporaryDirectory(prefix="zoitechat-signing-", dir=os.environ.get("RUNNER_TEMP")) as temporary:
directory = Path(temporary)
keychain = directory / "signing.keychain-db"
keychain_password = secrets.token_urlsafe(32)
certificate = directory / "signing.p12"
api_key = directory / "notary.p8"
write_private(certificate, credentials.certificate)
write_private(api_key, (credentials.api_key + "\n").encode())
try:
execute(["security", "create-keychain", "-p", keychain_password, keychain], "Create signing keychain")
execute(["security", "set-keychain-settings", "-lut", "21600", keychain], "Configure signing keychain")
execute(["security", "unlock-keychain", "-p", keychain_password, keychain], "Unlock signing keychain")
# Include both public Developer ID intermediates so signing does not
# depend on the runner having cached the leaf certificate's issuer.
for name in ("DeveloperIDCA.cer", "DeveloperIDG2CA.cer"):
intermediate = directory / name
try:
with urllib.request.urlopen("https://www.apple.com/certificateauthority/" + name, timeout=60) as response:
write_private(intermediate, response.read())
except OSError:
raise RuntimeError("Download of Apple's public Developer ID intermediate failed") from None
execute(["security", "import", intermediate, "-k", keychain], "Import Developer ID intermediate")
execute(["security", "import", certificate, "-P", credentials.password,
"-k", keychain, "-T", "/usr/bin/codesign", "-T", "/usr/bin/security"], "Import signing .p12")
execute(["security", "set-key-partition-list", "-S", "apple-tool:,apple:",
"-k", keychain_password, keychain], "Allow non-interactive code signing")
execute(["security", "list-keychains", "-d", "user", "-s", keychain, *original], "Add temporary signing keychain")
identities = execute(["security", "find-identity", "-v", "-p", "codesigning", keychain], "Find signing identity").stdout
yield keychain, api_key, resolve_identity(identities, credentials.identity)
finally:
# Hosted runners are discarded too, but clean up on every ordinary
# success/failure path and restore the original search list.
execute(["security", "list-keychains", "-d", "user", "-s", *original], "Restore keychain search list", check=False)
if keychain.exists():
result = execute(["security", "delete-keychain", keychain], "Delete signing keychain", check=False)
if result.returncode:
print("Warning: keychain deletion failed; temporary files will still be removed.", file=sys.stderr)
def decode_response(result: subprocess.CompletedProcess[str], operation: str) -> dict:
try:
response = json.loads(result.stdout)
except (json.JSONDecodeError, TypeError):
raise RuntimeError(f"{operation} did not return valid JSON (exit {result.returncode})") from None
if not isinstance(response, dict):
raise RuntimeError(f"{operation} returned an unexpected response")
return response
def save_json(path: Path, response: dict) -> None:
path.write_text(json.dumps(response, indent=2) + "\n")
def notarize(app: Path, output: Path, diagnostics: Path, credentials: Credentials, api_key: Path, timeout: str) -> None:
diagnostics.mkdir(parents=True, exist_ok=True)
authentication = ["--key", str(api_key), "--key-id", credentials.key_id, "--issuer", credentials.issuer]
with tempfile.TemporaryDirectory(prefix="zoitechat-notary-", dir=os.environ.get("RUNNER_TEMP")) as temporary:
submission_zip = Path(temporary) / "ZoiteChat-submission.zip"
execute(["ditto", "-c", "-k", "--keepParent", app, submission_zip], "Package notarization submission")
result = execute(["xcrun", "notarytool", "submit", submission_zip, *authentication,
"--output-format", "json"], "Submit notarization", check=False)
submission = decode_response(result, "Notarization submission")
save_json(diagnostics / "submission.json", submission)
if result.returncode or not submission.get("id"):
raise RuntimeError("Apple rejected the notarization submission; see submission.json")
try:
submission_id = str(uuid.UUID(submission["id"]))
except (ValueError, TypeError, AttributeError):
raise RuntimeError("Apple returned an invalid notarization submission ID") from None
print(f"Notarization submitted: {submission_id}", flush=True)
waited = execute(["xcrun", "notarytool", "wait", submission_id, *authentication,
"--timeout", timeout, "--output-format", "json"], "Wait for notarization", check=False)
try:
status = decode_response(waited, "Notarization wait")
except RuntimeError:
status = {"id": submission_id, "status": "Unknown", "exit_code": waited.returncode}
save_json(diagnostics / "status.json", status)
# Apple can provide a diagnostic log after processing. Fetch it for both
# successful and rejected submissions; never include credential files.
execute(["xcrun", "notarytool", "log", submission_id, *authentication,
diagnostics / "notary-log.json"], "Fetch notarization log", check=False)
if waited.returncode or status.get("status") != "Accepted":
raise RuntimeError("Notarization was not accepted or exceeded the wait limit; see the notarization diagnostic artifact. No release ZIP will be published")
execute(["xcrun", "stapler", "staple", app], "Staple notarization ticket")
execute(["xcrun", "stapler", "validate", app], "Validate notarization ticket")
execute(["codesign", "--verify", "--deep", "--strict", "--verbose=2", app], "Verify stapled app signature")
execute(["spctl", "--assess", "--type", "execute", "--verbose=2", app], "Assess Gatekeeper acceptance")
output.parent.mkdir(parents=True, exist_ok=True)
# Repack only after stapling. The submission ZIP is not the distributable ZIP.
execute(["ditto", "-c", "-k", "--keepParent", app, output], "Package notarized release")
print(f"Signed and notarized release: {output}", flush=True)
def main() -> None:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--check-config", action="store_true", help="Validate release settings without accessing Apple or a keychain")
parser.add_argument("--app", type=Path, default=Path("dist/ZoiteChat.app"))
parser.add_argument("--output", type=Path, default=Path("dist/ZoiteChat-signed-notarized.zip"))
parser.add_argument("--diagnostics", type=Path, default=Path("dist/notarization"))
parser.add_argument("--notary-timeout", default="60m")
args = parser.parse_args()
credentials = Credentials.from_environment()
if args.check_config:
print("Release signing and notarization settings are present and correctly formatted.")
return
if platform.system() != "Darwin":
parser.error("Signing/notarization requires the macOS runner and Xcode command-line tools")
if not args.app.is_dir() or args.output.exists():
parser.error("Provide a built .app and an output ZIP path that does not already exist")
if not re.fullmatch(r"[1-9]\d*[smh]", args.notary_timeout):
parser.error("Use a positive notarization timeout, such as 60m")
with signing_keychain(credentials) as (keychain, api_key, identity):
sign(args.app.resolve(), identity, Path(__file__).with_name("direct.entitlements"), keychain=keychain)
notarize(args.app.resolve(), args.output.resolve(), args.diagnostics.resolve(), credentials, api_key, args.notary_timeout)
if __name__ == "__main__":
try:
main()
except (RuntimeError, OSError):
# RuntimeError messages are authored above and do not contain secret argv.
error = sys.exc_info()[1]
if isinstance(error, RuntimeError):
sys.exit(f"macOS release failed: {error}")
sys.exit("macOS release failed: file or network operation failed")
except subprocess.CalledProcessError:
sys.exit("macOS release failed: code signing or bundle audit failed")
+49
View File
@@ -0,0 +1,49 @@
#!/usr/bin/env python3
"""Run the relocated bundle with temporary settings and no IRC auto-connect."""
from __future__ import annotations
import argparse
from pathlib import Path
import shutil
import subprocess
import tempfile
def main() -> None:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("app", type=Path)
args = parser.parse_args()
with tempfile.TemporaryDirectory(prefix="zoitechat-macos-smoke-") as directory:
# Include spaces to exercise dyld and GTK module-cache path handling.
app = Path(directory) / "Relocated App" / "ZoiteChat.app"
shutil.copytree(args.app.resolve(), app)
binary = app / "Contents/MacOS/zoitechat"
subprocess.run(["codesign", "--verify", "--deep", "--strict", str(app)], check=True)
subprocess.run([str(binary), "--version"], check=True, timeout=30)
config = Path(directory) / "test-config"
with (Path(directory) / "startup.log").open("w+") as log:
process = subprocess.Popen([str(binary), "--no-auto", "--cfgdir", str(config)], stdout=log, stderr=log)
try:
process.wait(timeout=10)
except subprocess.TimeoutExpired:
# Stop the isolated process after checking for early startup failure.
process.terminate()
try:
process.wait(timeout=10)
except subprocess.TimeoutExpired:
process.kill()
process.wait()
raise RuntimeError("GUI startup process did not terminate")
else:
log.seek(0)
raise RuntimeError(f"GUI exited before smoke interval (code {process.returncode}):\n{log.read()}")
log.seek(0)
messages = log.read()
print(messages)
for failure in ("Library not loaded:", "Symbol not found:", "cannot open display", "Cannot load bundled HTTPS trust database", "Bundled emoji font is missing", "Cannot relocate", "Error loading plugin:"):
if failure.lower() in messages.lower():
raise RuntimeError(f"Startup failure found: {failure}")
print("Relocated bundle stayed running for 10 seconds; manual interaction testing is still required.")
if __name__ == "__main__":
main()
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"><dict>
<key>com.apple.security.app-sandbox</key><true/>
<key>com.apple.security.network.client</key><true/>
<!-- DCC listening sockets and the optional ident service. -->
<key>com.apple.security.network.server</key><true/>
<key>com.apple.security.files.user-selected.read-write</key><true/>
<key>com.apple.security.files.downloads.read-write</key><true/>
</dict></plist>
Loaded 100 of 261 files, more files were not shown because too many files have changed in this diff. Show more