serverless-ircd 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yml +40 -3
- package/.github/workflows/deploy-aws.yml +1 -2
- package/.github/workflows/deploy-cf-tcp.yml +1 -5
- package/.github/workflows/deploy-cf.yml +1 -2
- package/.gitmodules +3 -0
- package/CHANGELOG.md +474 -0
- package/README.md +219 -22
- package/apps/aws-stack/README.md +33 -0
- package/apps/aws-stack/package.json +1 -1
- package/apps/aws-stack/src/aws-stack.ts +74 -9
- package/apps/aws-stack/tests/coverage-command.test.ts +155 -0
- package/apps/aws-stack/tests/stack.test.ts +2 -2
- package/apps/aws-stack/tests/synth-no-bundle.test.ts +129 -0
- package/apps/aws-stack/tests/vitest.setup.ts +18 -0
- package/apps/aws-stack/vitest.config.ts +3 -0
- package/apps/cf-tcp-container/package.json +1 -1
- package/apps/cf-tcp-container/src/config-loader.ts +6 -7
- package/apps/cf-tcp-container/src/container-server.ts +3 -0
- package/apps/cf-tcp-container/tests/config-loader.test.ts +10 -3
- package/apps/cf-tcp-container/wrangler.toml +1 -1
- package/apps/cf-worker/package.json +2 -1
- package/apps/cf-worker/src/origin-allowlist.ts +99 -0
- package/apps/cf-worker/src/worker.ts +89 -9
- package/apps/cf-worker/tests/fixtures/web-dist/app/index.html +18 -0
- package/apps/cf-worker/tests/fixtures/web-dist/index.html +18 -0
- package/apps/cf-worker/tests/origin-allowlist.test.ts +101 -0
- package/apps/cf-worker/tests/smoke.test.ts +28 -2
- package/apps/cf-worker/tests/ws-origin.test.ts +89 -0
- package/apps/cf-worker/vitest.config.ts +1 -1
- package/apps/cf-worker/wrangler.test.toml +23 -0
- package/apps/cf-worker/wrangler.toml +59 -3
- package/apps/local-cli/package.json +1 -1
- package/apps/local-cli/src/config-loader.ts +6 -6
- package/apps/local-cli/src/main.ts +20 -0
- package/apps/local-cli/src/server.ts +9 -7
- package/apps/local-cli/tests/e2e.test.ts +112 -28
- package/apps/local-cli/tests/tcp.test.ts +71 -19
- package/apps/local-cli/tests/ws-subprotocol.test.ts +12 -7
- package/apps/web/landing/index.html +217 -0
- package/apps/web/package.json +28 -0
- package/apps/web/scripts/build.mjs +125 -0
- package/apps/web/src/build-env.ts +43 -0
- package/apps/web/src/config-schema.ts +138 -0
- package/apps/web/static/config.json +28 -0
- package/apps/web/static/config.prod.json +28 -0
- package/apps/web/static/config.staging.json +28 -0
- package/apps/web/tests/build-env.test.ts +63 -0
- package/apps/web/tests/build-smoke.test.ts +103 -0
- package/apps/web/tests/config-schema.test.ts +432 -0
- package/apps/web/tests/workspace.test.ts +12 -0
- package/apps/web/tsconfig.json +10 -0
- package/apps/web/upstream/.browserslistrc +3 -0
- package/apps/web/upstream/.editorconfig +9 -0
- package/apps/web/upstream/.eslintignore +3 -0
- package/apps/web/upstream/.eslintrc.js +90 -0
- package/apps/web/upstream/.github/FUNDING.yml +12 -0
- package/apps/web/upstream/.github/ISSUE_TEMPLATE/bug_report.md +38 -0
- package/apps/web/upstream/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
- package/apps/web/upstream/.github/workflows/push.yml +26 -0
- package/apps/web/upstream/.github/workflows/release.yml +33 -0
- package/apps/web/upstream/.github/workflows/release_manual.yml +55 -0
- package/apps/web/upstream/.prettierrc.js +9 -0
- package/apps/web/upstream/.stylelintignore +5 -0
- package/apps/web/upstream/.stylelintrc.js +21 -0
- package/apps/web/upstream/LICENSE +201 -0
- package/apps/web/upstream/README.md +76 -0
- package/apps/web/upstream/babel.config.js +25 -0
- package/apps/web/upstream/crowdin.yml +3 -0
- package/apps/web/upstream/index.html +35 -0
- package/apps/web/upstream/jest.config.js +7 -0
- package/apps/web/upstream/jsdoc.json +22 -0
- package/apps/web/upstream/package.json +80 -0
- package/apps/web/upstream/scripts/pre-commit +43 -0
- package/apps/web/upstream/src/components/App.vue +516 -0
- package/apps/web/upstream/src/components/AppSettings.vue +587 -0
- package/apps/web/upstream/src/components/AutoComplete.vue +284 -0
- package/apps/web/upstream/src/components/Avatar.vue +105 -0
- package/apps/web/upstream/src/components/AwayStatusIndicator.vue +77 -0
- package/apps/web/upstream/src/components/BufferKey.vue +121 -0
- package/apps/web/upstream/src/components/BufferSettings.vue +97 -0
- package/apps/web/upstream/src/components/Captcha.vue +85 -0
- package/apps/web/upstream/src/components/ChannelBanlist.vue +96 -0
- package/apps/web/upstream/src/components/ChannelInfo.vue +159 -0
- package/apps/web/upstream/src/components/ChannelInvitelist.vue +276 -0
- package/apps/web/upstream/src/components/ChannelList.vue +345 -0
- package/apps/web/upstream/src/components/Container.vue +401 -0
- package/apps/web/upstream/src/components/ContainerHeader.vue +522 -0
- package/apps/web/upstream/src/components/ControlInput.vue +1016 -0
- package/apps/web/upstream/src/components/LoadingAnimation.vue +104 -0
- package/apps/web/upstream/src/components/MediaViewer.vue +199 -0
- package/apps/web/upstream/src/components/MessageInfo.vue +192 -0
- package/apps/web/upstream/src/components/MessageList.vue +1028 -0
- package/apps/web/upstream/src/components/MessageListAvatar.vue +20 -0
- package/apps/web/upstream/src/components/MessageListMessageCompact.vue +361 -0
- package/apps/web/upstream/src/components/MessageListMessageInline.vue +257 -0
- package/apps/web/upstream/src/components/MessageListMessageModern.vue +430 -0
- package/apps/web/upstream/src/components/NetworkSettings.vue +528 -0
- package/apps/web/upstream/src/components/Nicklist.vue +309 -0
- package/apps/web/upstream/src/components/NicklistUser.vue +181 -0
- package/apps/web/upstream/src/components/NotConnected.vue +271 -0
- package/apps/web/upstream/src/components/SelfUser.vue +283 -0
- package/apps/web/upstream/src/components/ServerSelector.vue +162 -0
- package/apps/web/upstream/src/components/ServerView.vue +126 -0
- package/apps/web/upstream/src/components/SettingsAdvanced.vue +232 -0
- package/apps/web/upstream/src/components/SettingsAliases.vue +121 -0
- package/apps/web/upstream/src/components/Sidebar.vue +429 -0
- package/apps/web/upstream/src/components/SidebarAboutBuffer.vue +288 -0
- package/apps/web/upstream/src/components/SidebarState.vue +145 -0
- package/apps/web/upstream/src/components/StartupError.vue +35 -0
- package/apps/web/upstream/src/components/StateBrowser.vue +420 -0
- package/apps/web/upstream/src/components/StateBrowserBuffer.vue +124 -0
- package/apps/web/upstream/src/components/StateBrowserNetwork.vue +823 -0
- package/apps/web/upstream/src/components/StateBrowserUsermenu.vue +162 -0
- package/apps/web/upstream/src/components/TypingStatusIndicator.vue +34 -0
- package/apps/web/upstream/src/components/TypingUsersList.vue +56 -0
- package/apps/web/upstream/src/components/UrlEmbed.vue +149 -0
- package/apps/web/upstream/src/components/UserBox.vue +728 -0
- package/apps/web/upstream/src/components/inputtools/Emoji.vue +67 -0
- package/apps/web/upstream/src/components/inputtools/TextStyle.vue +142 -0
- package/apps/web/upstream/src/components/startups/CommonLayout.vue +208 -0
- package/apps/web/upstream/src/components/startups/CustomServer.vue +495 -0
- package/apps/web/upstream/src/components/startups/KiwiBnc.vue +361 -0
- package/apps/web/upstream/src/components/startups/Personal.vue +317 -0
- package/apps/web/upstream/src/components/startups/Welcome.vue +544 -0
- package/apps/web/upstream/src/components/startups/ZncLogin.vue +229 -0
- package/apps/web/upstream/src/components/utils/InputConfirm.vue +68 -0
- package/apps/web/upstream/src/components/utils/InputPrompt.vue +118 -0
- package/apps/web/upstream/src/components/utils/InputText.vue +172 -0
- package/apps/web/upstream/src/components/utils/IrcInput.vue +579 -0
- package/apps/web/upstream/src/components/utils/PluginWrapper.vue +26 -0
- package/apps/web/upstream/src/components/utils/TabbedView.vue +149 -0
- package/apps/web/upstream/src/components/utils/TransitionExpand.vue +97 -0
- package/apps/web/upstream/src/helpers/Colours.js +128 -0
- package/apps/web/upstream/src/helpers/IrcdDiffs.js +26 -0
- package/apps/web/upstream/src/helpers/Md5.js +193 -0
- package/apps/web/upstream/src/helpers/Misc.js +401 -0
- package/apps/web/upstream/src/helpers/TextFormatting.js +237 -0
- package/apps/web/upstream/src/libs/AliasRewriter.js +157 -0
- package/apps/web/upstream/src/libs/AudioManager.js +60 -0
- package/apps/web/upstream/src/libs/BouncerMiddleware.js +247 -0
- package/apps/web/upstream/src/libs/BouncerProvider.js +551 -0
- package/apps/web/upstream/src/libs/ChathistoryMiddleware.js +153 -0
- package/apps/web/upstream/src/libs/ConfigLoader.js +94 -0
- package/apps/web/upstream/src/libs/EmojiProvider.js +46 -0
- package/apps/web/upstream/src/libs/GlobalApi.js +346 -0
- package/apps/web/upstream/src/libs/IPC.js +51 -0
- package/apps/web/upstream/src/libs/InputHandler.js +921 -0
- package/apps/web/upstream/src/libs/IrcClient.js +1506 -0
- package/apps/web/upstream/src/libs/Logger.js +71 -0
- package/apps/web/upstream/src/libs/Message.js +169 -0
- package/apps/web/upstream/src/libs/MessageFormatter.js +364 -0
- package/apps/web/upstream/src/libs/MessageParser.js +249 -0
- package/apps/web/upstream/src/libs/Notifications.js +78 -0
- package/apps/web/upstream/src/libs/ServerConnection.js +270 -0
- package/apps/web/upstream/src/libs/ServerSession.js +102 -0
- package/apps/web/upstream/src/libs/SoundBleep.js +20 -0
- package/apps/web/upstream/src/libs/StatePersistence.js +90 -0
- package/apps/web/upstream/src/libs/ThemeManager.js +128 -0
- package/apps/web/upstream/src/libs/TypingMiddleware.js +105 -0
- package/apps/web/upstream/src/libs/WindowTitle.js +63 -0
- package/apps/web/upstream/src/libs/batchedAdd.js +74 -0
- package/apps/web/upstream/src/libs/bufferTools.js +177 -0
- package/apps/web/upstream/src/libs/polyfill/Element.closest.js +18 -0
- package/apps/web/upstream/src/libs/renderers/Html.js +113 -0
- package/apps/web/upstream/src/libs/settingTools.js +31 -0
- package/apps/web/upstream/src/libs/state/BufferState.js +703 -0
- package/apps/web/upstream/src/libs/state/NetworkState.js +158 -0
- package/apps/web/upstream/src/libs/state/UserState.js +89 -0
- package/apps/web/upstream/src/libs/state/common.js +26 -0
- package/apps/web/upstream/src/libs/state.js +961 -0
- package/apps/web/upstream/src/libs/storage/Local.js +51 -0
- package/apps/web/upstream/src/main.js +566 -0
- package/apps/web/upstream/src/res/autocompleteCommands.js +31 -0
- package/apps/web/upstream/src/res/configTemplates.js +373 -0
- package/apps/web/upstream/src/res/globalStyle.css +277 -0
- package/apps/web/upstream/src/res/kiwiLoadingLogo.png +0 -0
- package/apps/web/upstream/src/res/locales/app.af-ZA.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.ar-SA.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.bg-BG.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.bs-BA.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.ca-ES.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.cs-CZ.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.da-DK.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.de-DE.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.dev.po +1246 -0
- package/apps/web/upstream/src/res/locales/app.el-GR.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.en-US.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.es-419.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.es-AR.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.es-EM.po +536 -0
- package/apps/web/upstream/src/res/locales/app.es-ES.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.es-US.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.eu-ES.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.fi-FI.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.fr-FR.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.gl-ES.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.he-IL.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.hi-IN.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.hu-HU.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.id-ID.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.it-IT.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.ja-JP.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.ko-KR.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.nl-NL.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.no-NO.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.pl-PL.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.pt-BR.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.pt-PT.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.ro-RO.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.ru-RU.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.sl-SI.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.sq-AL.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.sr-SP.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.sv-SE.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.tr-TR.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.uk-UA.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.vi-VN.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.zh-CN.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.zh-TW.po +1145 -0
- package/apps/web/upstream/src/res/localesList.json +27 -0
- package/apps/web/upstream/src/res/logo.png +0 -0
- package/apps/web/upstream/src/thirdparty/about.html +43 -0
- package/apps/web/upstream/src/thirdparty/index.js +5 -0
- package/apps/web/upstream/src/thirdparty/kiwiirccom.vue +185 -0
- package/apps/web/upstream/static/config.json +28 -0
- package/apps/web/upstream/static/emoticons/smile.png +0 -0
- package/apps/web/upstream/static/favicon.png +0 -0
- package/apps/web/upstream/static/highlight.mp3 +0 -0
- package/apps/web/upstream/static/highlight.ogg +0 -0
- package/apps/web/upstream/static/locales/.gitignore +2 -0
- package/apps/web/upstream/static/plugins/customise.html.example +10 -0
- package/apps/web/upstream/static/themes/coffee/theme.css +145 -0
- package/apps/web/upstream/static/themes/common/base.css +1064 -0
- package/apps/web/upstream/static/themes/dark/theme.css +196 -0
- package/apps/web/upstream/static/themes/default/theme.css +92 -0
- package/apps/web/upstream/static/themes/elite/theme.css +248 -0
- package/apps/web/upstream/static/themes/grayfox/theme.css +143 -0
- package/apps/web/upstream/static/themes/nightswatch/theme.css +367 -0
- package/apps/web/upstream/static/themes/osprey/theme.css +115 -0
- package/apps/web/upstream/static/themes/radioactive/theme.css +1211 -0
- package/apps/web/upstream/static/themes/sky/theme.css +107 -0
- package/apps/web/upstream/tests/unit/BatchAdd.spec.js +174 -0
- package/apps/web/upstream/tests/unit/MessageParser.spec.js +125 -0
- package/apps/web/upstream/tests/unit/Misc.spec.js +24 -0
- package/apps/web/upstream/tests/unit/NetworkState.spec.js +58 -0
- package/apps/web/upstream/tests/unit/StartupError.spec.js +20 -0
- package/apps/web/upstream/vue.config.js +150 -0
- package/apps/web/upstream/yarn.lock +9767 -0
- package/apps/web/vitest.config.ts +13 -0
- package/biome.json +2 -0
- package/docs/ADR-001-pure-reducers-and-effect-system.md +74 -0
- package/docs/ADR-002-location-of-authority.md +82 -0
- package/docs/ADR-003-durable-object-sharding.md +110 -0
- package/docs/ADR-004-dynamodb-schema.md +96 -0
- package/docs/ADR-005-wss-only-transport-v1.md +83 -0
- package/docs/ADR-006-sasl-mechanism-scope.md +96 -0
- package/docs/ADR-007-deterministic-ports.md +82 -0
- package/docs/ADR-008-monorepo-tooling.md +60 -0
- package/docs/ADR-009-dual-transport-wss-and-irc-tls.md +138 -0
- package/docs/ADR-Index.md +15 -0
- package/docs/AWS-Adapter-Architecture.md +496 -0
- package/docs/AWS-Deployment.md +1275 -0
- package/docs/AWS-TCP-Deployment.md +410 -0
- package/docs/Cloudflare-Deployment-Guide.md +719 -0
- package/docs/Cloudflare-TCP-Deployment.md +510 -0
- package/docs/Home.md +16 -0
- package/docs/Observability.md +87 -0
- package/docs/PlanExtensions.md +812 -0
- package/docs/PlanIRCv3Websocket.md +503 -0
- package/docs/PlanWebClient.md +451 -0
- package/docs/Release-Process.md +443 -0
- package/docs/WebClientGuide.md +535 -0
- package/package.json +2 -2
- package/packages/aws-adapter/package.json +1 -1
- package/packages/aws-adapter/src/handlers/default.ts +3 -0
- package/packages/aws-adapter/src/handlers/nlb-stream.ts +9 -0
- package/packages/aws-adapter/src/serialize.ts +29 -1
- package/packages/aws-adapter/tests/account-store-dynamo.test.ts +49 -8
- package/packages/aws-adapter/tests/account-store.test.ts +4 -7
- package/packages/aws-adapter/tests/aws-harness.ts +5 -2
- package/packages/aws-adapter/tests/aws-integration.test.ts +8 -4
- package/packages/aws-adapter/tests/aws-runtime.test.ts +79 -0
- package/packages/aws-adapter/tests/config-loader.test.ts +11 -2
- package/packages/aws-adapter/tests/connect.test.ts +9 -9
- package/packages/aws-adapter/tests/disconnect-fanout.test.ts +3 -4
- package/packages/aws-adapter/tests/global-setup.ts +55 -30
- package/packages/aws-adapter/tests/gone-exception.test.ts +4 -9
- package/packages/aws-adapter/tests/handlers.test.ts +90 -4
- package/packages/aws-adapter/tests/message-store.test.ts +6 -5
- package/packages/aws-adapter/tests/nlb-stream.test.ts +60 -8
- package/packages/cf-adapter/package.json +2 -1
- package/packages/cf-adapter/src/connection-do.ts +3 -0
- package/packages/cf-adapter/src/serialize.ts +25 -4
- package/packages/cf-adapter/tests/cf-harness.ts +12 -1
- package/packages/cf-adapter/tests/cf-runtime.test.ts +269 -0
- package/packages/cf-adapter/tests/channel-do.test.ts +117 -0
- package/packages/cf-adapter/tests/config-loader.test.ts +49 -0
- package/packages/cf-adapter/tests/connection-do-pure.test.ts +91 -0
- package/packages/cf-adapter/tests/d1-account-store.test.ts +48 -0
- package/packages/cf-adapter/tests/registry-do.test.ts +104 -0
- package/packages/cf-adapter/tests/serialize.test.ts +308 -0
- package/packages/cf-adapter/vitest.config.ts +1 -1
- package/packages/in-memory-runtime/package.json +1 -1
- package/packages/irc-core/package.json +1 -1
- package/packages/irc-core/reports/mutation/mutation.html +342 -0
- package/packages/irc-core/src/caps/capabilities.ts +37 -3
- package/packages/irc-core/src/caps/index.ts +1 -0
- package/packages/irc-core/src/caps/sts.ts +84 -0
- package/packages/irc-core/src/commands/account-notify.ts +53 -0
- package/packages/irc-core/src/commands/away.ts +9 -3
- package/packages/irc-core/src/commands/cap.ts +23 -5
- package/packages/irc-core/src/commands/chathistory.ts +40 -11
- package/packages/irc-core/src/commands/index.ts +34 -0
- package/packages/irc-core/src/commands/isupport.ts +53 -0
- package/packages/irc-core/src/commands/mode.ts +8 -1
- package/packages/irc-core/src/commands/monitor.ts +327 -0
- package/packages/irc-core/src/commands/multiline.ts +256 -0
- package/packages/irc-core/src/commands/pre-away.ts +112 -0
- package/packages/irc-core/src/commands/privmsg.ts +19 -12
- package/packages/irc-core/src/commands/read-marker.ts +108 -0
- package/packages/irc-core/src/commands/registration.ts +8 -0
- package/packages/irc-core/src/commands/sasl.ts +24 -1
- package/packages/irc-core/src/commands/tagmsg.ts +71 -1
- package/packages/irc-core/src/commands/whois.ts +5 -0
- package/packages/irc-core/src/config.ts +65 -10
- package/packages/irc-core/src/effects.ts +14 -0
- package/packages/irc-core/src/ports.ts +389 -0
- package/packages/irc-core/src/protocol/index.ts +14 -0
- package/packages/irc-core/src/protocol/labeled-response.ts +133 -0
- package/packages/irc-core/src/protocol/numerics.ts +15 -0
- package/packages/irc-core/src/protocol/outbound.ts +16 -1
- package/packages/irc-core/src/protocol/standard-replies.ts +277 -0
- package/packages/irc-core/src/state/connection.ts +32 -1
- package/packages/irc-core/src/types.ts +112 -0
- package/packages/irc-core/stryker.commands.conf.json +1 -2
- package/packages/irc-core/tests/away-store.test.ts +73 -0
- package/packages/irc-core/tests/batch.test.ts +15 -0
- package/packages/irc-core/tests/caps/capabilities.test.ts +119 -2
- package/packages/irc-core/tests/caps/sts.test.ts +123 -0
- package/packages/irc-core/tests/commands/account-notify.test.ts +171 -0
- package/packages/irc-core/tests/commands/away.test.ts +85 -2
- package/packages/irc-core/tests/commands/cap.test.ts +165 -0
- package/packages/irc-core/tests/commands/chathistory.test.ts +148 -1
- package/packages/irc-core/tests/commands/echo-message.test.ts +9 -3
- package/packages/irc-core/tests/commands/isupport.test.ts +80 -0
- package/packages/irc-core/tests/commands/mode.test.ts +72 -0
- package/packages/irc-core/tests/commands/monitor.test.ts +411 -0
- package/packages/irc-core/tests/commands/multiline.test.ts +428 -0
- package/packages/irc-core/tests/commands/pre-away.test.ts +241 -0
- package/packages/irc-core/tests/commands/privmsg.test.ts +154 -3
- package/packages/irc-core/tests/commands/read-marker.test.ts +211 -0
- package/packages/irc-core/tests/commands/registration.test.ts +64 -0
- package/packages/irc-core/tests/commands/sasl.test.ts +116 -2
- package/packages/irc-core/tests/commands/tagmsg.test.ts +357 -0
- package/packages/irc-core/tests/commands/whois.test.ts +34 -0
- package/packages/irc-core/tests/config.test.ts +103 -13
- package/packages/irc-core/tests/labeled-response.test.ts +181 -0
- package/packages/irc-core/tests/message-tags.test.ts +114 -0
- package/packages/irc-core/tests/monitor-store.test.ts +200 -0
- package/packages/irc-core/tests/numerics.test.ts +13 -0
- package/packages/irc-core/tests/parser.test.ts +109 -0
- package/packages/irc-core/tests/read-marker-store.test.ts +108 -0
- package/packages/irc-core/tests/standard-replies.test.ts +412 -0
- package/packages/irc-core/tests/types.test.ts +27 -0
- package/packages/irc-server/package.json +1 -1
- package/packages/irc-server/src/actor.ts +450 -3
- package/packages/irc-server/src/dispatch.ts +108 -16
- package/packages/irc-server/tests/actor.test.ts +1432 -39
- package/packages/irc-server/tests/dispatch.test.ts +263 -2
- package/packages/irc-test-support/package.json +1 -1
- package/packages/irc-test-support/src/index.ts +6 -0
- package/packages/irc-test-support/src/scenarios.ts +24 -2
- package/packages/irc-test-support/src/test-config.ts +54 -0
- package/packages/irc-test-support/tests/test-config.test.ts +51 -0
- package/tools/ci-hardening/package.json +1 -1
- package/tools/ci-hardening/tests/ddb-local.test.ts +82 -0
- package/tools/load-test/package.json +33 -0
- package/tools/load-test/src/client.ts +351 -0
- package/tools/load-test/src/config.ts +313 -0
- package/tools/load-test/src/harness.ts +116 -0
- package/tools/load-test/src/main.ts +120 -0
- package/tools/load-test/src/metrics.ts +168 -0
- package/tools/load-test/src/report.ts +106 -0
- package/tools/load-test/tests/client.test.ts +212 -0
- package/tools/load-test/tests/config.test.ts +152 -0
- package/tools/load-test/tests/framing.test.ts +37 -0
- package/tools/load-test/tests/harness.test.ts +165 -0
- package/tools/load-test/tests/metrics.test.ts +174 -0
- package/tools/load-test/tests/report.test.ts +161 -0
- package/tools/load-test/tests/smoke.test.ts +67 -0
- package/tools/load-test/tsconfig.build.json +12 -0
- package/tools/load-test/tsconfig.test.json +10 -0
- package/tools/load-test/vitest.config.ts +29 -0
- package/tools/tcp-ws-forwarder/package.json +1 -1
|
@@ -0,0 +1,551 @@
|
|
|
1
|
+
'kiwi public';
|
|
2
|
+
|
|
3
|
+
import _ from 'lodash';
|
|
4
|
+
import Logger from '@/libs/Logger';
|
|
5
|
+
import bouncerMiddleware from '@/libs/BouncerMiddleware';
|
|
6
|
+
|
|
7
|
+
let log = Logger.namespace('BouncerProvider.js');
|
|
8
|
+
|
|
9
|
+
export default class BouncerProvider {
|
|
10
|
+
constructor(state) {
|
|
11
|
+
this.state = state;
|
|
12
|
+
|
|
13
|
+
// This is the network that will be used to control the bouncer
|
|
14
|
+
this.controllerNetwork = null;
|
|
15
|
+
|
|
16
|
+
// The detected credentials for the BNC
|
|
17
|
+
this.bnc = {
|
|
18
|
+
enabled: false,
|
|
19
|
+
username: '',
|
|
20
|
+
password: '',
|
|
21
|
+
server: '',
|
|
22
|
+
port: 6667,
|
|
23
|
+
tls: false,
|
|
24
|
+
direct: false,
|
|
25
|
+
path: '',
|
|
26
|
+
registered: false,
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// If enabled, new IRC connections will be re-routed through the bouncer
|
|
30
|
+
this.rewriteConnections = true;
|
|
31
|
+
|
|
32
|
+
// A snapshot of the current networks. Compared against to detect changed networks
|
|
33
|
+
this.networksSnapshot = Object.create(null);
|
|
34
|
+
|
|
35
|
+
// If we are currently monitoring for network settings changes
|
|
36
|
+
this.monitoringChanges = false;
|
|
37
|
+
|
|
38
|
+
state.$on('irc.motd', this.onNetworkMotd.bind(this));
|
|
39
|
+
state.$on('irc.bouncer state', this.onNetworkState.bind(this));
|
|
40
|
+
state.$on('irc.bouncer networks', this.onBouncerNetworks.bind(this));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
enable(server, port, tls, direct, path) {
|
|
44
|
+
log(`Using a Bouncer Provider with server ${server}:${port}`);
|
|
45
|
+
|
|
46
|
+
this.bnc.server = server;
|
|
47
|
+
this.bnc.port = port || 6667;
|
|
48
|
+
this.bnc.tls = !!tls;
|
|
49
|
+
this.bnc.direct = !!direct;
|
|
50
|
+
this.bnc.path = path || '';
|
|
51
|
+
this.bnc.enabled = true;
|
|
52
|
+
|
|
53
|
+
// get the bnc controller network
|
|
54
|
+
const bncNetwork = this.state.networks.find((network) => network.is_bnc);
|
|
55
|
+
|
|
56
|
+
// the bnc controller network password is saved in the format <username>:<password>.
|
|
57
|
+
// if there is a bnc controller network with a password, use these credentials
|
|
58
|
+
// for the bnc connection.
|
|
59
|
+
if (bncNetwork?.connection?.password) {
|
|
60
|
+
let [username, password] = this.parseBncCredentials(bncNetwork.connection.password);
|
|
61
|
+
this.bnc.username = username;
|
|
62
|
+
this.bnc.password = password;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Enable BOUNCER on existing connections
|
|
66
|
+
this.state.networks.forEach((network) => {
|
|
67
|
+
network.ircClient.use(bouncerMiddleware());
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// this.monitorNetworkChanges();
|
|
71
|
+
this.listenToState();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Try to get a connected network that can be used to control the bouncer
|
|
75
|
+
getController() {
|
|
76
|
+
if (this.controllerNetwork && this.controllerNetwork.state === 'connected') {
|
|
77
|
+
return this.controllerNetwork;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
this.controllerNetwork = null;
|
|
81
|
+
for (let i = 0; i < this.state.networks.length; i++) {
|
|
82
|
+
let network = this.state.networks[i];
|
|
83
|
+
let client = network.ircClient;
|
|
84
|
+
if (network.state === 'connected' && client.network.cap.isEnabled('bouncer')) {
|
|
85
|
+
this.controllerNetwork = network;
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return this.controllerNetwork;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async onNetworkMotd(event, network) {
|
|
94
|
+
let client = network.ircClient;
|
|
95
|
+
|
|
96
|
+
if (!this.bnc.enabled) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (!client.network.cap.isEnabled('bouncer')) {
|
|
101
|
+
log.debug(`BOUNCER cap not available on network ${network.name}, not using for BNC`);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Set the bncnetid if the network upstream exists and we havn't already set it
|
|
106
|
+
if (client.bnc.hasNetwork() && !network.connection.bncnetid) {
|
|
107
|
+
network.connection.bncnetid = client.bnc.tags().netid;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Use this initial network password for other network connections
|
|
111
|
+
if (!this.bnc.username) {
|
|
112
|
+
let [username, password] = this.parseBncCredentials(network.connection.password);
|
|
113
|
+
this.bnc.username = username;
|
|
114
|
+
this.bnc.password = password;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// If this is a BNC network, sync it before anything else so that we get all its info
|
|
118
|
+
// and buffer states as soon as possible
|
|
119
|
+
if (client.bnc.hasNetwork()) {
|
|
120
|
+
await this.syncBncNetwork(network);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// If this is the controller network, add all other networks from the bouncer
|
|
124
|
+
if (!network.connection.bncnetid) {
|
|
125
|
+
await this.initAndAddNetworks(network);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
onNetworkState(event, network) {
|
|
130
|
+
let isController = this.getController() === network;
|
|
131
|
+
// We get connection state changes advertised on all bouncer connections, so only handle
|
|
132
|
+
// the ones that come from the controller network since we only need to deal with it once.
|
|
133
|
+
if (isController && event.state === 'disconnected') {
|
|
134
|
+
// Mimick any network disconnections by closing this connection too
|
|
135
|
+
let effectedNetwork = this.state.getNetworkFromBncNetId(event.networkId);
|
|
136
|
+
if (effectedNetwork) {
|
|
137
|
+
effectedNetwork.ircClient.connection.transport.close();
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async initAndAddNetworks(network) {
|
|
143
|
+
let client = network.ircClient;
|
|
144
|
+
|
|
145
|
+
this.bnc.registered = true;
|
|
146
|
+
|
|
147
|
+
// hide the empty (non-network) controller network
|
|
148
|
+
if (!network.ircClient.bnc.hasNetwork()) {
|
|
149
|
+
network.hidden = true;
|
|
150
|
+
network.is_bnc = true;
|
|
151
|
+
} else {
|
|
152
|
+
network.hidden = false;
|
|
153
|
+
network.is_bnc = false;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// onBouncerNetworks will pick up the event handler for this
|
|
157
|
+
client.bnc.getNetworks();
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
onBouncerNetworks(bncNetworks) {
|
|
161
|
+
log.debug(`Got ${bncNetworks.length} networks from the BNC`, bncNetworks);
|
|
162
|
+
bncNetworks.forEach((bncNet) => this.addNetworkToState(bncNet));
|
|
163
|
+
|
|
164
|
+
// Remove any networks we have locally but no longer exist on the BNC
|
|
165
|
+
this.state.networks.forEach((existingNet) => {
|
|
166
|
+
let isNetworkInBncList = !!bncNetworks.find((n) => (
|
|
167
|
+
n.networkId === existingNet.connection.bncnetid
|
|
168
|
+
));
|
|
169
|
+
|
|
170
|
+
if (!existingNet.is_bnc && !isNetworkInBncList) {
|
|
171
|
+
log.debug(`Network '${existingNet.name}' (${existingNet.id}) was not in the BNC, removing locally`);
|
|
172
|
+
this.state.removeNetwork(existingNet.id);
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// start monitoring network changes
|
|
177
|
+
this.monitorNetworkChanges();
|
|
178
|
+
|
|
179
|
+
if (this.state.getActiveBuffer()) {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
if (!bncNetworks.length) {
|
|
183
|
+
// BNC has no network, lets add one to help the user
|
|
184
|
+
const net = this.state.addNetwork('Network', '', {});
|
|
185
|
+
net.showServerBuffer('settings');
|
|
186
|
+
} else {
|
|
187
|
+
for (let i = 0; i < this.state.networks.length; i++) {
|
|
188
|
+
const net = this.state.networks[i];
|
|
189
|
+
if (!net.is_bnc) {
|
|
190
|
+
// The first network that is not the BNC control network
|
|
191
|
+
net.showServerBuffer('settings');
|
|
192
|
+
break;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
async syncBncNetwork(bncNetwork) {
|
|
199
|
+
let client = bncNetwork.ircClient;
|
|
200
|
+
|
|
201
|
+
log.debug(`Syncing network ${bncNetwork.name} from the BNC`);
|
|
202
|
+
|
|
203
|
+
let buffers = await client.bnc.getBuffers(bncNetwork.connection.bncnetid);
|
|
204
|
+
buffers.forEach((buffer) => {
|
|
205
|
+
// The list of buffers also include the network name. Make use of it and make sure our
|
|
206
|
+
// network name is up to date while we can. It may have changed elsewhere
|
|
207
|
+
if (bncNetwork.name !== buffer.network) {
|
|
208
|
+
log(`Detected network name change while syncing buffers. ${bncNetwork.name} > ${buffer.network}`);
|
|
209
|
+
bncNetwork.name = buffer.network;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
let newBuffer = this.state.addBuffer(bncNetwork.id, buffer.name);
|
|
213
|
+
if (!newBuffer) {
|
|
214
|
+
// The BNC might be giving up bad buffer names or something, so just make sure
|
|
215
|
+
// that it's handled if it ever coccurs
|
|
216
|
+
log.error(`Couldn't add BNC buffer, '${buffer.name}'`, buffer);
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (buffer.joined) {
|
|
221
|
+
newBuffer.enabled = true;
|
|
222
|
+
newBuffer.joined = true;
|
|
223
|
+
} else {
|
|
224
|
+
newBuffer.enabled = false;
|
|
225
|
+
newBuffer.joined = false;
|
|
226
|
+
}
|
|
227
|
+
if (buffer.seen) {
|
|
228
|
+
newBuffer.last_read = (new Date(buffer.seen)).getTime();
|
|
229
|
+
}
|
|
230
|
+
if (buffer.notify) {
|
|
231
|
+
newBuffer.setting('alert_on', buffer.notify);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
newBuffer.topic = buffer.topic || '';
|
|
235
|
+
|
|
236
|
+
if (bncNetwork.state === 'connected' && newBuffer.isChannel() && newBuffer.joined) {
|
|
237
|
+
client.raw('NAMES ' + newBuffer.name);
|
|
238
|
+
client.who(newBuffer.name);
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
// Remove any existing buffers that we no longer have on the bouncer
|
|
243
|
+
bncNetwork.buffers.forEach((clientBuffer) => {
|
|
244
|
+
if (!clientBuffer.isChannel() && !clientBuffer.isQuery()) {
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
let existingBuffers = buffers.filter((bncBuffer) => (
|
|
249
|
+
bncBuffer.name.toLowerCase() === clientBuffer.name.toLowerCase()
|
|
250
|
+
));
|
|
251
|
+
|
|
252
|
+
if (existingBuffers.length === 0) {
|
|
253
|
+
this.state.removeBuffer(clientBuffer);
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
async addNetworkToState(network) {
|
|
259
|
+
// Expects network to be in the format of:
|
|
260
|
+
// {
|
|
261
|
+
// "networkId":"xyz",
|
|
262
|
+
// "name":"freenode",
|
|
263
|
+
// "channel":"1",
|
|
264
|
+
// "connected":"1",
|
|
265
|
+
// "host":"irc.freenode.net",
|
|
266
|
+
// "port":"6667",
|
|
267
|
+
// "tls":"0",
|
|
268
|
+
// "nick":"notprawn99829"
|
|
269
|
+
// },
|
|
270
|
+
let net = this.state.getNetworkFromBncNetId(network.networkId);
|
|
271
|
+
if (!net) {
|
|
272
|
+
net = this.state.addNetwork(network.name, network.nick || '', {
|
|
273
|
+
server: network.host,
|
|
274
|
+
port: network.port,
|
|
275
|
+
tls: network.tls,
|
|
276
|
+
password: network.password || '',
|
|
277
|
+
bncnetid: network.networkId,
|
|
278
|
+
username: network.user,
|
|
279
|
+
account_password: network.account_password,
|
|
280
|
+
});
|
|
281
|
+
} else {
|
|
282
|
+
// Make sure our existing network is all up to date
|
|
283
|
+
net.name = network.name;
|
|
284
|
+
net.connection.server = network.host;
|
|
285
|
+
net.connection.port = parseInt(network.port, 10);
|
|
286
|
+
net.connection.tls = network.tls;
|
|
287
|
+
net.connection.nick = network.account || network.nick;
|
|
288
|
+
net.connection.password = network.password || '';
|
|
289
|
+
net.nick = network.currentNick || network.nick || '';
|
|
290
|
+
net.password = network.account_password;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
net.editable_name = true;
|
|
294
|
+
return net;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Keep a snapshot of what the current networks are. They will be periodically
|
|
298
|
+
// compared with the active networks to see if anything has changed before
|
|
299
|
+
// saving those changes.
|
|
300
|
+
snapshotCurrentNetworks() {
|
|
301
|
+
this.networksSnapshot = Object.create(null);
|
|
302
|
+
|
|
303
|
+
this.state.networks.forEach((network) => {
|
|
304
|
+
if (!network.connection.bncnetid) {
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
this.networksSnapshot[network.connection.bncnetid] = {
|
|
309
|
+
bncnetid: network.connection.bncnetid,
|
|
310
|
+
network: network.name,
|
|
311
|
+
host: network.connection.server,
|
|
312
|
+
port: network.connection.port,
|
|
313
|
+
tls: network.connection.tls,
|
|
314
|
+
account: network.connection.nick,
|
|
315
|
+
account_password: network.password,
|
|
316
|
+
server_password: network.connection.password,
|
|
317
|
+
nick: network.connection.nick,
|
|
318
|
+
username: network.username,
|
|
319
|
+
};
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Compare the current networks with our previously saved snapshot of
|
|
324
|
+
// networks. Save any changes to the bouncer
|
|
325
|
+
saveState() {
|
|
326
|
+
let controller = this.getController();
|
|
327
|
+
if (!controller) {
|
|
328
|
+
log.debug('No controller available to save networks');
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
this.state.networks.forEach((network) => {
|
|
333
|
+
// don't save an empty controller to the network.
|
|
334
|
+
// we can't use hasNetwork alone because that requires ircClient to be
|
|
335
|
+
// connected, and we save new nets before they are connected
|
|
336
|
+
if (this.getController() === network && !network.ircClient.bnc.hasNetwork()) {
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Don't save any hidden networks. This includes the BNC controller network
|
|
341
|
+
if (network.hidden) {
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// Every network needs a valid name
|
|
346
|
+
if (!network.name.match(/^[a-zA-Z_0-9.]+$/)) {
|
|
347
|
+
log.debug(`Network ${network.id} has an invalid name. Not saving to the BNC`);
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
let bncnetid = network.connection.bncnetid;
|
|
352
|
+
let snapshot = this.networksSnapshot[bncnetid] || {};
|
|
353
|
+
let tags = {};
|
|
354
|
+
|
|
355
|
+
if (network.name !== snapshot.network) {
|
|
356
|
+
tags.network = network.name;
|
|
357
|
+
}
|
|
358
|
+
if (network.connection.server !== snapshot.host) {
|
|
359
|
+
tags.host = network.connection.server;
|
|
360
|
+
}
|
|
361
|
+
if (network.connection.port !== snapshot.port) {
|
|
362
|
+
tags.port = network.connection.port;
|
|
363
|
+
}
|
|
364
|
+
if (network.connection.tls !== snapshot.tls) {
|
|
365
|
+
tags.tls = network.connection.tls;
|
|
366
|
+
}
|
|
367
|
+
if (network.password !== snapshot.account_password) {
|
|
368
|
+
tags.account_password = network.password;
|
|
369
|
+
}
|
|
370
|
+
if (network.connection.nick !== snapshot.account) {
|
|
371
|
+
tags.account = network.connection.nick;
|
|
372
|
+
}
|
|
373
|
+
if (network.connection.password !== snapshot.server_password) {
|
|
374
|
+
tags.password = network.connection.password;
|
|
375
|
+
}
|
|
376
|
+
if (network.connection.nick !== snapshot.nick) {
|
|
377
|
+
tags.nick = network.connection.nick;
|
|
378
|
+
}
|
|
379
|
+
if (network.username !== snapshot.username) {
|
|
380
|
+
tags.user = network.username;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// A newly added network would not have a snapshot name (bncnetid) property set yet.
|
|
384
|
+
// Only save the network if we've entered connection info.
|
|
385
|
+
if (!snapshot.bncnetid && tags.host && tags.port && tags.nick) {
|
|
386
|
+
log(`Saving new network ${network.name} to the BNC`);
|
|
387
|
+
// ?? network.connection.bncname = network.name;
|
|
388
|
+
controller.ircClient.bnc.addNetwork(
|
|
389
|
+
network.name,
|
|
390
|
+
tags.host,
|
|
391
|
+
tags.port,
|
|
392
|
+
tags.tls,
|
|
393
|
+
tags.nick,
|
|
394
|
+
tags.user,
|
|
395
|
+
tags.password,
|
|
396
|
+
).then((networkInfo) => {
|
|
397
|
+
network.connection.bncnetid = networkInfo.networkId;
|
|
398
|
+
network.name = networkInfo.network;
|
|
399
|
+
});
|
|
400
|
+
} else if (snapshot.bncnetid && Object.keys(tags).length > 0) {
|
|
401
|
+
log(`Updating network ${network.name} on the BNC`);
|
|
402
|
+
controller.ircClient.bnc.saveNetwork(bncnetid, tags);
|
|
403
|
+
}
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
this.snapshotCurrentNetworks();
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
monitorNetworkChanges() {
|
|
410
|
+
if (this.monitoringChanges) {
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
this.monitoringChanges = true;
|
|
414
|
+
|
|
415
|
+
this.snapshotCurrentNetworks();
|
|
416
|
+
|
|
417
|
+
let debouncedSaveState = _.debounce(this.saveState.bind(this), 2000);
|
|
418
|
+
this.state.$watch('networks', debouncedSaveState, { deep: true });
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
listenToState() {
|
|
422
|
+
let state = this.state;
|
|
423
|
+
|
|
424
|
+
// Just before we connect to a network, make sure the BNC is saved and connected to
|
|
425
|
+
// it or at least trying to connect.
|
|
426
|
+
// Ie. Quickly creating a network and hitting connect before it's had time to
|
|
427
|
+
// save itself to the bouncer
|
|
428
|
+
state.$on('network.connecting', (event) => {
|
|
429
|
+
// Redirect the connection towards the bouncer with the network specific password
|
|
430
|
+
let network = event.network;
|
|
431
|
+
if (this.bnc.enabled && this.rewriteConnections) {
|
|
432
|
+
let netname = network.name;
|
|
433
|
+
|
|
434
|
+
let ircClient = network.ircClient;
|
|
435
|
+
ircClient.options.host = this.bnc.server;
|
|
436
|
+
ircClient.options.port = this.bnc.port;
|
|
437
|
+
ircClient.options.tls = this.bnc.tls;
|
|
438
|
+
|
|
439
|
+
// Only re-write the server password for non-bnc controller networks
|
|
440
|
+
if (this.bnc.password && !event.network.is_bnc) {
|
|
441
|
+
let password = `${this.bnc.username}/${netname}:${this.bnc.password}`;
|
|
442
|
+
ircClient.options.password = password;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// The SASL auth already happens on the BNC, we only use it for UI purposes in kiwi
|
|
446
|
+
ircClient.options.account = {};
|
|
447
|
+
|
|
448
|
+
network.connection.direct = this.bnc.direct;
|
|
449
|
+
ircClient.options.path = this.bnc.path;
|
|
450
|
+
}
|
|
451
|
+
});
|
|
452
|
+
state.$on('network.connecting', (event) => {
|
|
453
|
+
let controller = this.getController();
|
|
454
|
+
if (!controller) {
|
|
455
|
+
log.debug('No controller available to save network states');
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
this.saveState();
|
|
460
|
+
|
|
461
|
+
let network = event.network;
|
|
462
|
+
|
|
463
|
+
if (network.connection.bncnetid) {
|
|
464
|
+
controller.ircClient.raw('BOUNCER connect ' + network.connection.bncnetid);
|
|
465
|
+
}
|
|
466
|
+
});
|
|
467
|
+
state.$on('irc.motd', (event, network) => {
|
|
468
|
+
network.buffers.forEach((buffer) => {
|
|
469
|
+
if (buffer.isChannel() && buffer.enabled && buffer.joined) {
|
|
470
|
+
network.ircClient.who(buffer.name);
|
|
471
|
+
}
|
|
472
|
+
});
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
state.$on('network.new', (event) => {
|
|
476
|
+
let network = event.network;
|
|
477
|
+
|
|
478
|
+
// Enable BOUNCER on this connection
|
|
479
|
+
network.ircClient.use(bouncerMiddleware());
|
|
480
|
+
|
|
481
|
+
network.editable_name = true;
|
|
482
|
+
|
|
483
|
+
// If network is attached to a bouncer do nothing
|
|
484
|
+
// (use the name provided by the bouncer)
|
|
485
|
+
if (network.connection.bncnetid) {
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
let existingNet = true;
|
|
490
|
+
|
|
491
|
+
// append a number to the network name. E.g. "Network, Network2,..."
|
|
492
|
+
// while there is a network with that name
|
|
493
|
+
let currentNum = 1;
|
|
494
|
+
let tryNetworkName;
|
|
495
|
+
while (existingNet) {
|
|
496
|
+
if (network.name && currentNum === 1) {
|
|
497
|
+
// don't append the number 1 if there is a custom name
|
|
498
|
+
tryNetworkName = network.name;
|
|
499
|
+
} else {
|
|
500
|
+
tryNetworkName = (network.name || 'Network') + currentNum;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
existingNet = _.find(state.networks, {
|
|
504
|
+
name: tryNetworkName,
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
if (!existingNet || existingNet === network) {
|
|
508
|
+
network.name = tryNetworkName;
|
|
509
|
+
break;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
currentNum++;
|
|
513
|
+
}
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
state.$on('network.removed', (event) => {
|
|
517
|
+
let controller = this.getController();
|
|
518
|
+
if (!controller) {
|
|
519
|
+
log.debug('No controller available to save network states');
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
if (event.network.connection.bncnetid) {
|
|
524
|
+
controller.ircClient.bnc.removeNetwork(event.network.connection.bncnetid);
|
|
525
|
+
}
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
state.$on('buffer.close', (event) => {
|
|
529
|
+
let buffer = event.buffer;
|
|
530
|
+
let network = event.buffer.getNetwork();
|
|
531
|
+
let bncnetid = network.connection.bncnetid;
|
|
532
|
+
|
|
533
|
+
let controller = this.getController();
|
|
534
|
+
if (!controller) {
|
|
535
|
+
log.debug('No controller available to save buffer states');
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
if (bncnetid) {
|
|
540
|
+
controller.ircClient.bnc.closeBuffer(bncnetid, buffer.name);
|
|
541
|
+
}
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
parseBncCredentials(bncNetworkPassword) {
|
|
546
|
+
let [username, password] = bncNetworkPassword.split(':');
|
|
547
|
+
username = username.split('/')[0];
|
|
548
|
+
|
|
549
|
+
return [username, password];
|
|
550
|
+
}
|
|
551
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
'kiwi public';
|
|
2
|
+
|
|
3
|
+
/** @module */
|
|
4
|
+
|
|
5
|
+
import Logger from '@/libs/Logger';
|
|
6
|
+
import * as Misc from '@/helpers/Misc';
|
|
7
|
+
|
|
8
|
+
const log = Logger.namespace('chathistory');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Adds the CHATHISTORY IRCv3 spec to irc-framework
|
|
12
|
+
*/
|
|
13
|
+
export default function chathistoryMiddleware() {
|
|
14
|
+
return function middleware(client, rawEvents, parsedEvents) {
|
|
15
|
+
client.requestCap('draft/chathistory');
|
|
16
|
+
addFunctionsToClient(client);
|
|
17
|
+
parsedEvents.use(theMiddleware);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
function theMiddleware(command, event, client, next) {
|
|
21
|
+
if (command.toLowerCase() === 'batch end chathistory' && client.chathistory) {
|
|
22
|
+
let target = event.params[0];
|
|
23
|
+
client.chathistory.batchCallbacks.resolve(target, event);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// This is sent as 'unknown command', its been structured this way so hopefully it
|
|
27
|
+
// will still work if a fail handler is ever created in irc-fw
|
|
28
|
+
if (event?.command?.toLowerCase() === 'fail' && event.params[0].toLowerCase() === 'chathistory') {
|
|
29
|
+
client.chathistory.batchCallbacks.resolve(event.params[3]);
|
|
30
|
+
|
|
31
|
+
if (event.params[1].toLowerCase() === 'invalid_target') {
|
|
32
|
+
// suppress invalid target errors
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
next();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function addFunctionsToClient(client) {
|
|
42
|
+
const history = client.chathistory = {};
|
|
43
|
+
|
|
44
|
+
history.batchCallbacks = {
|
|
45
|
+
callbacks: Object.create(null),
|
|
46
|
+
queue: [],
|
|
47
|
+
queueActive: false,
|
|
48
|
+
add(cb, target, type, ...args) {
|
|
49
|
+
this.callbacks[target.toUpperCase()] = this.callbacks[target.toUpperCase()] || [];
|
|
50
|
+
this.callbacks[target.toUpperCase()].push(cb);
|
|
51
|
+
this.queue.push({
|
|
52
|
+
target,
|
|
53
|
+
type,
|
|
54
|
+
args,
|
|
55
|
+
});
|
|
56
|
+
if (!this.queueActive) {
|
|
57
|
+
this.queueNext();
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
resolve(target, value) {
|
|
61
|
+
const targetCallbacks = this.callbacks[target.toUpperCase()] || [];
|
|
62
|
+
const cb = targetCallbacks.shift();
|
|
63
|
+
|
|
64
|
+
if (!targetCallbacks.length) {
|
|
65
|
+
// last callback, cleanup callbacks
|
|
66
|
+
delete this.callbacks[target.toUpperCase()];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (cb) {
|
|
70
|
+
cb(value);
|
|
71
|
+
} else if (history.isSupported()) {
|
|
72
|
+
// inspircd currently does not support chathistory
|
|
73
|
+
// but sends chathistory batches when joining channels
|
|
74
|
+
log.error('chathistory got a resolve but no associated callback');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
this.queueNext();
|
|
78
|
+
},
|
|
79
|
+
queueNext() {
|
|
80
|
+
this.queueActive = true;
|
|
81
|
+
|
|
82
|
+
const nextRequest = this.queue.shift();
|
|
83
|
+
if (!nextRequest) {
|
|
84
|
+
this.queueActive = false;
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
client.raw('CHATHISTORY', nextRequest.type, nextRequest.target, ...nextRequest.args);
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
// supports (ISUPPORT) is used by kiwibnc, the spec and unreal's implementation uses CAP
|
|
93
|
+
history.isSupported = () => !!client.network.supports('draft/chathistory') || client.network.cap.isEnabled('draft/chathistory');
|
|
94
|
+
|
|
95
|
+
history.before = (target, dateOrTime) => new Promise((resolve) => {
|
|
96
|
+
if (!history.isSupported()) {
|
|
97
|
+
resolve();
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
history.batchCallbacks.add(resolve, target, 'BEFORE', messageReference(dateOrTime), '50');
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
history.after = (target, dateOrTime) => new Promise((resolve) => {
|
|
105
|
+
if (!history.isSupported()) {
|
|
106
|
+
resolve();
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
history.batchCallbacks.add(resolve, target, 'AFTER', messageReference(dateOrTime), '50');
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
history.latest = (target, dateOrTime) => new Promise((resolve) => {
|
|
114
|
+
if (!history.isSupported()) {
|
|
115
|
+
resolve();
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
history.batchCallbacks.add(resolve, target, 'LATEST', messageReference(dateOrTime), '50');
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
history.around = (target, dateOrTime) => new Promise((resolve) => {
|
|
123
|
+
if (!history.isSupported()) {
|
|
124
|
+
resolve();
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
history.batchCallbacks.add(resolve, target, 'AROUND', messageReference(dateOrTime), '50');
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
history.between = (target, fromDateOrTime, toDateOrTime) => new Promise((resolve) => {
|
|
132
|
+
if (!history.isSupported()) {
|
|
133
|
+
resolve();
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const fromRef = messageReference(fromDateOrTime);
|
|
138
|
+
const toRef = messageReference(toDateOrTime);
|
|
139
|
+
history.batchCallbacks.add(resolve, target, 'BETWEEN', fromRef, toRef, 50);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
function messageReference(inp) {
|
|
143
|
+
if (typeof inp === 'object') {
|
|
144
|
+
return 'timestamp=' + Misc.dateIso(inp);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (inp === '*') {
|
|
148
|
+
return '*';
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return 'msgid=' + inp;
|
|
152
|
+
}
|
|
153
|
+
}
|