[Plugin] BetterChat v5.0.2

BetterChat

BetterChat is an unofficial addon for TeamSpeak 6 and aims to provide a better chat experience for the TeamSpeak 6 client. It enables support for BBCodes, improving messages sent by TeamSpeak 3 users, and automatic rich embeds for video, audio, image and twitter content. It works in both compact and detailed view.

BBCode support

BetterChat readds support for BBCodes in chat, just like in TeamSpeak 3. Currently, the following tags are supported:

NOTE
For BetterChat 5.0.0 and later, Markdown and BBCode syntax can no longer be mixed.
When a message contains any BBTag, all Markdown formatting will be ignored.

Common

Name Syntax Example
bold [b]bold[/b] bold
color [color=#FFA500]hexcode[/color] or [color=orange]css color[/color] color
highlight [highlight]highlighted text[/highlight] highlight
italic [i]italic[/i] italic
strike [s]strike[/s] strike
underline [u]underline[/u] underline
url [u]https://example.com[/u] or [url=https://example.com]text[/url] url

Lists and Tables

Name Syntax Example
list [list]
  [li]first item[/li]
  [li]second item[/li]
[/list]

List tags optionally support setting the list item style type:
  • Unordered lists: “disc”, “circle”, “square”
  • Ordered lists: “1”, “i”, “I”, “a”, “A”
Example:

[list=square]
  [li]first item[/li]
  [li]second item[/li]
[/list]

List items can be turned into task items by setting their tag value to either “o” for unckecked or “x” for checked task items. Example:

[list]
  [li=x]checked item[/li]
  [li=o]unchecked item[/li]
[/list]
list
styled list
tasklist
ol [ol]
  [li]first item[/li]
  [li]second item[/li]
[/ol]
ol
table [table]
  [tr]
    [th]first header[/th]
    [th]second header[/th]
  [/tr]
  [tr]
    [td]first value[/td]
    [td]second value[/td]
  [/tr]
[/table]
ul [ul]
  [li]first item[/li]
  [li]second item[/li]
[/ul]
ul

Quotes and Callouts

Name Syntax Example
caution [caution]caution[/caution]
important [important]important[/important]
note [note]note[/note]
quote [quote]quoted text[/quote] or [quote=“title”]quoted text[/quote]
tip [tip]tip[/tip]
warning [warning]warning[/warning]

Code and Math

Name Syntax Example
code [code]text[/code] code
latex [latex]2+2=4[/latex] latex
math or tex [math]2+2=4[/math] or [tex]2+2=4[/tex] math
pre [pre]inline code[/pre] pre
subscript [sub]subscript[/sub] subscript
superscript [sup]superscript[/sup] superscript

Reveal

Name Syntax Example
details [details]collapsed text[/details] or [details=“summary”]collapsed text[/details]
spoiler [spoiler]spoiler[/spoiler] spoiler

Heading

Name Syntax Example
h1 [h1]heading 1[/h1] heading1
h2 [h2]heading 2[/h2] heading2
h3 [h3]heading 3[/h3] heading3
h4 [h4]heading 4[/h4] heading4
h5 [h5]heading 5[/h5] heading5
h6 [h6]heading 6[/h6] heading6

Alignment

Name Syntax Example
center [center]centered text[/center] center
justify [justify]justified text[/justify] justify
left [left]left aligned text[/left] left
right [right]right aligned text[/right] right

Others

Name Syntax Example
footnote or fn text[footnote]footnote[/footnote]
hr first paragraph[hr]second paragraph hr
mermaid or mmd [mermaid]
sequenceDiagram
  Alice->>+John: Hello John, how are you?
  Alice->>+John: John, can you hear me?
  John–>>-Alice: Hi Alice, I can hear you
  John–>>-Alice: I feel great!
[/mermaid]

Tag values can optionally be surrounded by double-quotes in order to include closing square bracket characters. The double-quotes themselves can be escaped using a backslash. Example: [details="A quoted tag value including a \" and a ] character"].

Rich Embeds

BetterChat supports automatic rich embeds for any website, including dedicated embeds for video, audio, image and twitter content. Due to technical limitations, not all video and audio formats are supported at the moment. A list of supported audio and video formats can be found here.

Video Embed

Audio Embed

Twitter Embed

Generic Embed

Download and Installation

NOTE
The addon needs to be reinstalled after every TeamSpeak update

A download and installation instructions can be found on GitHub including an installer. Note that this addon modifies your existing TeamSpeak 6 installation. An installer for TeamSpeak 5 can be found here.

Configuration

BetterChat can be enabled and disabled while TeamSpeak is running. Simply navigate to the Chats category of the TeamSpeak settings. There you can toggle individual features, like BBCode support or Rich Embeds, or enable and disable the addon entirely.

Acknowledgements

This addon is inspired by the TeamSpeak UNOFFICIAL Plugin Installer by Gamer9200

17 Likes

So, I didn’t look too deep at your code, but it looks really well written!
It seems you manipulate the actual DOM the same way I do.
For me, this results in some problems with the virtual scroller of the chat when rendering Twitter embeds.
Technically you can access the vuejs virtual scroller content and manipulate the render function directly. This would even eliminate the need for the MutationObserver and greatly improve performance. Seeing your dedication to this project, this would be an awesome next step. :stuck_out_tongue:

Also, your README suggests that MP4 (and others) are supported while in fact, it can’t be, as TeamSpeak’s CEF is compiled without proprietary codec support.

6 Likes

Thank you!

I would be very thankful if you would show me a proof of concept!

Good catch! I thought that the website would only list the available codecs. Technically you can get mp4 support in ts5 if you recompile cef with propietary codecs enabled, although its pretty complicated (but it works).

1 Like

It took some considerable time to figure all this vuejs stuff out, but I got there!
By modifying the component loader in the vendors.js file you can manipulate components before they are created or mounted. In this POC I opted to manipulate the tsv-virtual-list-item component. Keep in mind that the component IDs generated by webpack sadly change per update and thus you will need to reverse the main.js after each update.

POC

vendor.js after formatting

--- a/vendor.js
+++ b/vendor.js
@@ -2,7 +2,125 @@

-function e(r) { 
-  var o = n[r];
-  if (void 0 !== o) return o.exports;
-  var i = (n[r] = { id: r, loaded: !1, exports: {} });
-  return t[r].call(i.exports, i, i.exports, e), (i.loaded = !0), i.exports;
-}
+function oldE(r) {
+  var o = n[r];
+  if (void 0 !== o) return o.exports;
+  var i = (n[r] = { id: r, loaded: !1, exports: {} });
+  return t[r].call(i.exports, i, i.exports, e), (i.loaded = !0), i.exports;
+}
+
+const linkRegex =
+  /[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/;
+
+const contentTypeMap = {
+  jpg: "image/jpeg",
+  jpeg: "image/jpeg",
+  png: "image/png",
+  gif: "image/gif",
+  webp: "image/webp",
+  svg: "image/svg+xml",
+};
+
+function e(r) {
+  let result = oldE(r);
+  if (r === 661163) {
+    result = {
+      s: function () {
+        var e = this,
+          t = e._self._c;
+        e._self._setupProxy;
+
+        const item = e.item;
+
+        const children = [
+          e.item
+            ? t(
+                e.component,
+                e._g(
+                  e._b(
+                    {
+                      key: e.uniqueId,
+                      tag: "component",
+                      style: { opacity: e.finalHeight ? "1" : "0" },
+                      attrs: { role: "listitem" },
+                    },
+                    "component",
+                    e.filteredProps,
+                    !1
+                  ),
+                  e.wrappedListeners
+                )
+              )
+            : e._e(),
+        ];
+
+        try {
+          if (
+            !item.isSystem &&
+            !item.parsedItem?.hideText &&
+            !(item.parsedItem?.attachments?.length || false)
+          ) {
+            const text = item.original || item.body;
+            if (text.match(linkRegex)) {
+              console.error(this);
+              const extension = text.split(".").pop().split("?")[0];
+              const filename = text.split("/").pop().split("?")[0];
+              if (
+                ["jpg", "jpeg", "png", "gif", "webp", "svg"].includes(extension)
+              ) {
+                children.push(
+                  t(
+                    "div",
+                    {
+                      staticClass: "test",
+                      style: { "padding-left": "70px" },
+                    },
+                    [
+                      t("img", {
+                        attrs: { src: text },
+                        style: {
+                          width: "100%",
+                          "max-width": "350px",
+                          cursor: "pointer",
+                        },
+                        on: {
+                          click() {
+                            e.$root.appController.events.onInvokeLightboxEmitter.fire(
+                              {
+                                invocation: {
+                                  url: text,
+                                  thumbnailUrl: text,
+                                  contentType: contentTypeMap[extension],
+                                  contentSize: 0,
+                                  footerActions: [],
+                                  headerLabel: filename,
+                                  bodyLabel: "ᴱᵐᵇᵉᵈ ᵖᵒʷᵉʳᵉᵈ ᵇʸ ᴳᵃᵐᵉʳ⁹²⁰⁰⁰",
+                                },
+                              }
+                            );
+                          },
+                        },
+                      }),
+                    ]
+                  )
+                );
+              }
+            }
+          }
+        } catch (e) {
+          console.error(e);
+        }
+
+        return t(
+          "div",
+          {
+            ref: "itemRef",
+            staticClass: "tsv-virtual-list-item",
+            style: e.itemStyles,
+          },
+          children
+        );
+      },
+      x: result.x,
+    };
+  }
+  return result;
+}

This will crudely embed images and allow you to click on them just like other shared images.
TeamSpeak_FP3MfLvkhp

I actually did this once. But sadly you can neither distribute such a self-compiled version nor can you expect the users to do so. Also I wouldn’t want to compile this for a windows target…

EDIT: changed the POC to do something actually useful.
EDIT2: I just couldn’t stop myself…
Now the embed uses the native overlay to display a larger version of the image when clicked on.

4 Likes

I think this could also be done without modifying the vendor.js. You can access the vue instance with document.body.querySelector("#app").__vue__ or call $0.__vue__ on any other component.

2 Likes

Well, I’m not that proficient with vuejs + webpack compiled and bundled code.
So I don’t think you could patch the component loader this way.
Nonetheless, whether you modify the index.html or vendor.js is not much of a difference, is it?

My main problem was that I needed to modify the component loader before the vendor.js was actually loaded in order for it to actually use the patched version. Otherwise, the original version was just everywhere in the call stack and referenced from everywhere directly.

2 Likes

awesome work!
sadly i couldn’t make it work with channel description, only chats.

We updated the Installer and BetterChat to be compatible with the latest version of TeamSpeak (5.0.0-beta72). We also fixed the issue with the vuejs virtual scroller (hopefully).

3 Likes

We updated the Installer and BetterChat to be compatible with the latest version of TeamSpeak (5.0.0-beta73).

1 Like

We updated the Installer and BetterChat to be compatible with the latest version of TeamSpeak (5.0.0-beta74).

1 Like

We updated the Installer and BetterChat to be compatible with the latest version of TeamSpeak (5.0.0-beta75).

1 Like

We updated the Installer and BetterChat to be compatible with the latest version of TeamSpeak (5.0.0-beta76).

2 Likes

You said you updated the installer to version 76 but the installer is not releasing the new version, can you send us a new link so we can get the correct installer?
image
image
image

1 Like

I see that I have now installed the installer, it is working, everything is OK, but can you make the Plugin read these Chat effects in the descriptions of a server’s rooms?
Follow the example below:

We updated the Installer and BetterChat to be compatible with the latest version of TeamSpeak (5.0.0-beta77).

Sorry for the late reply, I didn’t see your message! When I post about a version update in this thread, it is available for download. I noticed that GitHub does some strange ordering with the releases, where the latest version is not always at the top. I will update description to link to the latest release.

EDIT: I added them to the update notice.

Thanks for the suggestion. I will look into it.

BBCodes are supported natively by TS5 in server and channel descriptions, on both TS5 servers and TS3 servers. However, your description looks like it is not properly formatted. For example, the headline is missing a closing [/b] tag. Additionally, tables are not supported by TS5 and neither by BetterChat.

The Installer is now available for TeamSpeak 6, along with a new version of BetterChat.

2 Likes

Hey, thank you! tiktok also?