Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 52 additions & 65 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,91 +1,78 @@
# Twitcher
# Twitcher for Redot

> **Redot compatibility fork:** This branch preserves Twitcher's Godot support while targeting
> Redot LTS 26.2. The editor plugin, setup wizard, and runtime `TwitchService` are validated with
> Redot `26.2.stable.official.4f5b14aba`.
[![Redot 26.2](https://img.shields.io/badge/Redot-26.2-EA4335?style=flat-square)](https://github.com/Redot-Engine/redot-engine/releases/tag/redot-26.2-stable)
[![Redot compatibility](https://github.com/dominicbytes/twitcher/actions/workflows/redot-compatibility.yml/badge.svg)](https://github.com/dominicbytes/twitcher/actions/workflows/redot-compatibility.yml)
[![License](https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square)](LICENSE)

[![Godot Asset Library](https://img.shields.io/badge/Godot%20Asset%20Library-Twitcher-blue?style=flat-square)](https://godotengine.org/asset-library/asset/2629) <!-- Replace YOUR_ASSET_ID -->
[![License](https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square)](https://github.com/kanimaru/twitcher/blob/v2/LICENSE) <!-- Assuming MIT License -->
[![Twitch](https://img.shields.io/badge/Support_on_Twitch-kani_dev-purple?style=flat-square&logo=twitch)](https://www.twitch.tv/kani_dev/)
[![Documentation](https://img.shields.io/badge/Twitcher-Documentation-purple?style=flat-square&logo=readthedocs)](https://twitcher.kani.dev/)
**Twitch integration for Redot 26.2 LTS.**

**Seamless Twitch Integration for Godot 4.4+ and Redot 26.2**
Twitcher for Redot connects Redot games, overlays, and applications to Twitch. It supports
EventSub, the Helix API, chat commands, OAuth flows, channel-point rewards, emotes, badges, and
Cheermotes.

Twitcher provides a comprehensive toolkit to effortlessly connect your Godot Engine games,
overlays, or applications to the Twitch platform. Integrate real-time chat, respond to events like
follows and subscriptions, manage rewards, handle chat commands, and utilize the full Twitch API with ease.
This is an independent Redot project derived from
[kani_dev's original Twitcher](https://github.com/kanimaru/twitcher). It is maintained separately
and is not intended to merge back into the original project. Twitcher for Redot preserves the
original public API and `res://addons/twitcher` paths while maintaining and testing Redot support.

## Key Features
## Features

* **Modern Twitch Integration:** Utilizes EventSub for real-time events and the Helix API for robust interactions (moving away from deprecated IRC features for core functionality).
* **Simplified Authentication:** Supports multiple OAuth flows (Authorization Code, Client Credentials, Device Code) with helpers for secure token management.
* **Easy Event Handling:** Dedicated `TwitchEventListener` node to react to specific EventSub events (Follows, Subs, Cheers, Rewards, etc.).
* **API Coverage:** Auto-generated, type-safe wrapper methods for the Twitch Helix REST API.
* **Chat Command Framework:** `TwitchCommand` nodes for defining and handling chat commands with permission checks. Includes an automated `!help` command generator.
* **Editor Tools:** Built-in helpers for configuring OAuth Scopes, EventSub subscriptions, and testing credentials directly within the Godot editor.
* **Media Loading:** Handles fetching and caching Twitch Emotes (including animated GIFs via optional transformers), Badges, and Cheermotes as Godot `SpriteFrames`.
- EventSub listeners for follows, subscriptions, cheers, rewards, and other Twitch events
- Generated, typed wrappers for the Twitch Helix REST API
- OAuth Authorization Code, Client Credentials, and Device Code flows
- Twitch chat commands with permission checks and help generation
- Redot editor tools for authentication, scopes, EventSub configuration, and rewards
- Twitch emote, badge, and Cheermote loading as Redot `SpriteFrames`

## Installation

1. **Get the Addon:**
* **Recommended (AssetLib):** Search for "Twitcher" in the Godot AssetLib tab and click Download.
* **Manual (GitHub):** Download from [GitHub](https://github.com/kanimaru/twitcher/releases). Extract the `addons/twitcher` folder into your project's directory.
* **Important:** The addon *must* reside in the exact path `res://addons/twitcher` for internal resources to load correctly.
2. **Enable Plugin:** Go to `Project -> Project Settings -> Plugins` and check the `Enable` box next to "Twitcher".

### Redot

Copy `addons/twitcher` to the same path in your Redot project, then enable Twitcher under
`Project -> Project Settings -> Plugins`. This fork uses the same public API and scene paths as
upstream Twitcher.
## Requirements

The development project can be checked with:
- [Redot 26.2 LTS](https://github.com/Redot-Engine/redot-engine/releases/tag/redot-26.2-stable)
- A Twitch developer application for features that require authentication

```powershell
redot --headless --path . --import
redot --headless --path . --script res://tests/redot_compatibility.gd
```

The probe loads the editor plugin, both setup-wizard variants, and an instantiated runtime
`TwitchService`. Live Twitch authentication and EventSub delivery require Twitch credentials and
are intentionally separate integration tests.

## Quick Start & Documentation
## Installation

1. **Run Setup Wizard:** After enabling the plugin, the easiest way to configure authentication is via the Setup Wizard:
* Navigate to `Project -> Tools -> Twitcher Setup`.
* Follow the on-screen instructions to enter your Twitch Application credentials and select required OAuth scopes.
2. **Explore the Documentation:** For detailed guides, tutorials, API reference, and advanced configuration (like setting up GIF support), please refer to the **[Full Twitcher Documentation](https://twitcher.kani.dev)**.
1. Download or clone this repository.
2. Copy `addons/twitcher` into your Redot project at exactly `res://addons/twitcher`.
3. In Redot, open **Project → Project Settings → Plugins** and enable **Twitcher for Redot**.
4. Open **Project → Tools → Twitcher → Setup** to configure your Twitch application and scopes.

## Image Transformers (for Animated Emotes)
## Documentation

Godot doesn't natively support animated GIFs. Twitcher uses configurable "Image Transformers" to handle them:
The [upstream Twitcher documentation](https://twitcher.kani.dev/) describes the public API and
core workflows shared by this fork. Where its editor screenshots or engine-version guidance
differs, use Redot 26.2 and the setup path above.

* **`TwitchImageTransformer` (Default):** Static images only. Works out-of-the-box.
* **`MagickImageTransformer`:** Requires [ImageMagick](https://imagemagick.org) to be installed separately. Converts GIFs to `SpriteFrames`.
* **`NativeImageTransformer`:** Experimental, uses native gdscript implementation (based on [vbousquet/godot-gif-importer](https://github.com/vbousquet/godot-gif-importer)). No external programs needed, but may struggle with malformed GIFs.
## Animated emotes

**See the [Full Documentation](https://twitcher.kani.dev/core-nodes/twitch-image-transformer.html)** for instructions on how to configure and use `MagickImageTransformer` or `NativeImageTransformer`.
Twitcher offers three image transformers:

## Support
- `TwitchImageTransformer`: static images; works without external tools
- `MagickImageTransformer`: GIF support through a separate
[ImageMagick](https://imagemagick.org/) installation
- `NativeImageTransformer`: experimental GDScript GIF support based on
[vbousquet/godot-gif-importer](https://github.com/vbousquet/godot-gif-importer)

Need help or have questions? Find kani_dev streaming development and answering questions on [Twitch](https://www.twitch.tv/kani_dev/). Feel free to open an Issue on GitHub for bugs or feature requests.
## Development and validation

## Development
Run the Redot compatibility checks from the repository root:

To prevent accidental commit of your Twitch `client_id` and `client_secret`, this repository includes a pre-commit hook and a GitHub Action.
This is only for Twitcher development. In your project it's fine to commit them because they got encrypted beforehand.
When you want to share your project with other devs, you should also share the key so that the other devs can use
the credentials.
```powershell
redot --headless --path . --import
redot --headless --path . --script res://tests/redot_compatibility.gd
```

### Setting up the local Pre-commit Hook
The probe loads the editor plugin and setup scenes and instantiates the runtime `TwitchService`.
Live OAuth, Twitch API, and EventSub delivery require credentials and remain integration tests.

To enable the local pre-commit hook that automatically clears secrets from your OAuth settings before committing, run the following command in your terminal:
To enable the local secret-clearing pre-commit hook:

```bash
git config core.hooksPath .githooks
```

## License
## Original project and license

Twitcher is released under the MIT License. See the [LICENSE](https://github.com/kanimaru/twitcher/blob/master/LICENSE) file for details.
Twitcher for Redot is derived from the original
[kanimaru/twitcher](https://github.com/kanimaru/twitcher), created by kani_dev. This repository is
an independent Redot continuation and does not replace or represent the original project. Both are
distributed under the [MIT License](LICENSE).
2 changes: 1 addition & 1 deletion addons/twitcher/chat/twitch_command_help.gd
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func _on_command_receive(from_username: String, info: TwitchCommandInfo, args: P
twitch_api.send_whisper(message_body, message["to_user_id"], message["from_user_id"])


## Needed because when you debug and change code etc. Godot will add the commands multiple times.
## Needed because Redot can add the commands multiple times while debugging and reloading scripts.
func cleanup_redundant_commands() -> void:
var command_set: Dictionary = {}
for command in TwitchCommand.ALL_COMMANDS:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ func request(path: String, method: int, body: Variant = "", content_type: String
var req: BufferedHTTPClient.RequestData = client.request(api_host + path, method, header, request_body)
var res: BufferedHTTPClient.ResponseData = await client.wait_for_request(req)

# Try to fix Godot TLS Bug
# Work around an engine TLS issue.
if res.result == 5:
return await retry(req, res, path, method, body, content_type, error_count + 1)

Expand Down
2 changes: 1 addition & 1 deletion addons/twitcher/editor/api_generator/twitch_gen_field.gd
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ var _is_typed_array: bool:
get(): return _is_array && _type.begins_with("#")


## Couple of names from the Twitch API are messed up like keywords for godot or numbers
## Some Twitch API names conflict with GDScript keywords or begin with numbers.
func _update_name(val: String) -> void:
_original_name = val
match val:
Expand Down
2 changes: 1 addition & 1 deletion addons/twitcher/editor/project_setting_property.gd
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func as_select(values: Array[String], optional: bool = true) -> ProjectSettingPr
return _add_type_def(TYPE_STRING, enum_hint, hint_string)


# Won't work in godot 4.4 the resource is not loaded when you select the project and it will just drop it out of the list.
# The resource is not loaded when selecting the project, so it would be dropped from the list.
#func as_resoruce(resource_name: StringName) -> ProjectSettingProperty:
# return _add_type_def(TYPE_OBJECT, PROPERTY_HINT_RESOURCE_TYPE, resource_name)

Expand Down
2 changes: 1 addition & 1 deletion addons/twitcher/editor/twitch_editor_node_utils.gd
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ static func create_media_loader(api: TwitchAPI) -> TwitchMediaLoader:
return media_loader


## Ugly but no better way in godot right now
## Redot currently provides no cleaner equivalent.
static func new_scene() -> bool:
var base_control: Control = EditorInterface.get_base_control()
var title_bar: Node = base_control.find_child("*EditorTitleBar*", true, false)
Expand Down
2 changes: 1 addition & 1 deletion addons/twitcher/example/chat/eventsub_chat.tscn
Original file line number Diff line number Diff line change
Expand Up @@ -220,5 +220,5 @@ metadata/_custom_type_script = "uid://bmluckfvgm1c2"

[node name="TwitchCommandRespond" type="Node" parent="TwitcherCommand" unique_id=1368511142]
script = ExtResource("22_viivw")
respond_message = "You can find Twitcher here: https://github.com/kanimaru/twitcher/"
respond_message = "You can find Twitcher for Redot here: https://github.com/dominicbytes/twitcher/"
metadata/_custom_type_script = "uid://dyslkjssasxnk"
2 changes: 1 addition & 1 deletion addons/twitcher/example/reward/reward.gd
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func setup_twitcher() -> void:
twitch_reward_service = TwitchRewardService.new(api, twitch_media_loader)
await twitch_reward_service.save_reward(TEST_REWARD)

# Prevent godot from just quitting to actually cleanup the test reward
# Prevent Redot from quitting before the test reward is cleaned up.
get_tree().auto_accept_quit = false


Expand Down
2 changes: 1 addition & 1 deletion addons/twitcher/generated/twitch_api.gd
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ func request(path: String, method: int, body: Variant = "", content_type: String
var req: BufferedHTTPClient.RequestData = client.request(api_host + path, method, header, request_body)
var res: BufferedHTTPClient.ResponseData = await client.wait_for_request(req)

# Try to fix Godot TLS Bug
# Work around an engine TLS issue.
if res.result == 5:
return await retry(req, res, path, method, body, content_type, error_count + 1)

Expand Down
2 changes: 1 addition & 1 deletion addons/twitcher/lib/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Because Godot doesn't have a good system to handle dependencies and
Because Redot doesn't have a package manager for addon dependencies and
I want to have standalone modules that could be technically their own plugins.

Requirements:
Expand Down
2 changes: 1 addition & 1 deletion addons/twitcher/lib/http/http_server.gd
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ func stop_listening() -> void:
func send_response(client: Client, response_code : String, body : PackedByteArray) -> void:
var peer: StreamPeerTCP = client.peer
peer.put_data(("HTTP/1.1 %s\r\n" % response_code).to_utf8_buffer())
peer.put_data("Server: Godot Engine (Twitcher)\r\n".to_utf8_buffer())
peer.put_data("Server: Redot Engine (Twitcher)\r\n".to_utf8_buffer())
peer.put_data(("Content-Length: %d\r\n"% body.size()).to_utf8_buffer())
peer.put_data("Connection: close\r\n".to_utf8_buffer())
peer.put_data("Content-Type: text/html charset=UTF-8\r\n".to_utf8_buffer())
Expand Down
2 changes: 1 addition & 1 deletion addons/twitcher/lib/oOuch/oauth.gd
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ func _process_implicit_request(client: OAuthHTTPServer.Client, server: OAuthHTTP
body: JSON.stringify(params)
})
.then(() => window.close())
.catch(err => console.error('Failed to send payload to Godot:', err));
.catch(err => console.error('Failed to send payload to Redot:', err));
</script>
</body>
</html>""" % oauth_setting.redirect_url
Expand Down
2 changes: 1 addition & 1 deletion addons/twitcher/lib/regex/regex_util.gd
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
## This one should be Godot Core but isn't sadly...
## This belongs in the engine core but is not available there.
class_name RegexUtil

static func escape(input:String) -> String:
Expand Down
2 changes: 1 addition & 1 deletion addons/twitcher/media/native_image_transformer.gd
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
@tool
extends TwitchImageTransformer

## Native GIF parser written in GDScript and ported to Godot 4. Most of the time stable but there
## Native GIF parser written in GDScript and ported for Redot. Most of the time stable but there
## are GIF's that may not work cause the file didn't follow the GIF specification.
class_name NativeImageTransformer

Expand Down
2 changes: 1 addition & 1 deletion addons/twitcher/media/twitch_image_transformer.gd
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
@tool
extends Resource

## Most simple image transformer that doesn't support GIF's uses builtin functionalities of godot.
## Basic image transformer without GIF support, using Redot's built-in image functionality.
class_name TwitchImageTransformer

static var _log: TwitchLogger = TwitchLogger.new("TwitchImageTransformer")
Expand Down
6 changes: 3 additions & 3 deletions addons/twitcher/plugin.cfg
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[plugin]

name="Twitcher"
description="A plugin to use the Twitch API in Godot and Redot."
author="kani_dev"
name="Twitcher for Redot"
description="Twitch API, EventSub, chat, OAuth, and media integration for Redot."
author="kani_dev; Redot port by dominicbytes"
version="2.5.1-redot.1"
script="plugin.gd"
2 changes: 1 addition & 1 deletion project.godot
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ config_version=5

[application]

config/name="Twitcher"
config/name="Twitcher for Redot"
run/main_scene="uid://5lyf04rwo1mf"
config/features=PackedStringArray("26.2", "Redot")
config/icon="res://addons/twitcher/assets/twitcher-icon.svg"
Expand Down
Loading