Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a new Channel API for experimental plugins feature #5141

Merged
merged 23 commits into from
Feb 3, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
f8c3e50
Fix lua::push<T>(L, T) for enums
Mm2PL Feb 1, 2024
0b14de3
Add UserData utility classes
Mm2PL Feb 1, 2024
b1d1230
Refactor script to allow including more files into the meta
Mm2PL Feb 1, 2024
4283d53
Allow adding other files into lua meta file
Mm2PL Feb 1, 2024
f53c64a
Add IWeakResource interface that has API methods expected from weak_ptr
Mm2PL Feb 1, 2024
e2b0f63
Create ChannelRef (c2.Channel)
Mm2PL Feb 1, 2024
0d83361
Add type information about c2.Channel
Mm2PL Feb 1, 2024
dcbe07e
Nuke c2.system_msg and c2.send_msg
Mm2PL Feb 1, 2024
f008530
Update documentation for Channel
Mm2PL Feb 1, 2024
59f13d2
changelog
Mm2PL Feb 1, 2024
b5d2df8
prettier more like uglier
Mm2PL Feb 1, 2024
0373497
Add lua::push(std::optional<T>)
Mm2PL Feb 2, 2024
5a5d36f
nit: use enable_if_t instead of _enable_if<...>::type
pajlada Feb 3, 2024
bee2f4d
Add support for retrieving Twitch-specific information about a channel
Mm2PL Feb 2, 2024
406ca14
Update lua type documentation
Mm2PL Feb 2, 2024
76dca5b
It's RoomModes
Mm2PL Feb 3, 2024
377350b
Document not being able to use non-Twitch channels for now
Mm2PL Feb 3, 2024
6562715
Hide C++ only methods from showing up in the meta.lua file
Mm2PL Feb 3, 2024
2c6041b
Merge branch 'feature/lua-channel-api' of github.com:Chatterino/chatt…
Mm2PL Feb 3, 2024
429dd14
Update documentation
Mm2PL Feb 3, 2024
dd17ad6
Ensure the smartptr behind UserData gets destroyed
Mm2PL Feb 3, 2024
3c50de1
Merge branch 'master' of github.com:Chatterino/chatterino2 into featu…
Mm2PL Feb 3, 2024
e07cc69
Update type defs
Mm2PL Feb 3, 2024
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
- Minor: Chatters from recent-messages are now added to autocompletion. (#5116)
- Minor: Added a _System_ theme that updates according to the system's color scheme (requires Qt 6.5). (#5118)
- Minor: Added support for the `{input.text}` placeholder in the **Split** -> **Run a command** hotkey. (#5130)
- Minor: Add a new Channel API for experimental plugins feature. (#5141)
- Bugfix: Fixed an issue where certain emojis did not send to Twitch chat correctly. (#4840)
- Bugfix: Fixed capitalized channel names in log inclusion list not being logged. (#4848)
- Bugfix: Trimmed custom streamlink paths on all platforms making sure you don't accidentally add spaces at the beginning or end of its path. (#4834)
Expand Down
34 changes: 32 additions & 2 deletions docs/chatterino.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,43 @@ declare module c2 {
channel_name: String;
}

enum Platform {
Twitch,
IRC,
}
enum ChannelType {
None,
Direct,
Twitch,
TwitchWhispers,
TwitchWatching,
TwitchMentions,
TwitchLive,
TwitchAutomod,
Irc,
Misc,
}

interface IWeakResource {
is_valid(): boolean;
}

class Channel implements IWeakResource {
is_valid(): boolean;
get_name(): string;
get_type(): ChannelType;
get_display_name(): string;
is_twitch_channel(): boolean;

static by_name(name: string, platform: Platform): null | Channel;
static by_twitch_id(id: string): null | Channel;
}
Copy link
Contributor

@Wissididom Wissididom Feb 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't there send_message and add_system_message missing, because send_msg and system_msg were removed a few lines below this class definition and in the plugin-meta.lua there are those functions defined as member of Channel?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are missing, thank you for reminding me of that


function log(level: LogLevel, ...data: any[]): void;
function register_command(
name: String,
handler: (ctx: CommandContext) => void
): boolean;
function send_msg(channel: String, text: String): boolean;
function system_msg(channel: String, text: String): boolean;

class CompletionList {
values: String[];
Expand Down
111 changes: 95 additions & 16 deletions docs/plugin-meta.lua
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@

c2 = {}

---@class IWeakResource

--- Returns true if the channel this object points to is valid.
--- If the object expired, returns false
--- If given a non-Channel object, it errors.
---@return boolean
function IWeakResource:is_valid() end


---@alias LogLevel integer
---@type { Debug: LogLevel, Info: LogLevel, Warning: LogLevel, Critical: LogLevel }
c2.LogLevel = {}
Expand All @@ -20,6 +29,92 @@ c2.EventType = {}
---@class CompletionList
---@field values string[] The completions
---@field hide_others boolean Whether other completions from Chatterino should be hidden/ignored.
-- Now including data from src/common/Channel.hpp.

---@alias ChannelType integer
---@type { None: ChannelType }
ChannelType = {}
-- Back to src/controllers/plugins/LuaAPI.hpp.
-- Now including data from src/controllers/plugins/api/ChannelRef.hpp.
--- This enum describes a platform for the purpose of searching for a channel.

---@alias Platform integer
---@type { Twitch: Platform }
Platform = {}
---@class Channel: IWeakResource

--- Get the content of the top object on Lua stack, usually first argument
--- to function as a ChannelPtr.
--- If the object given is not a userdatum or the pointer inside that
--- userdatum doesn't point to a Channel, a lua error is thrown.
---
--- @param expiredOk Should an expired return nullptr instead of erroring

--- Returns true if the channel this object points to is valid.
--- If the object expired, returns false
--- If given a non-Channel object, it errors.
---
---@return boolean success
function Channel:is_valid() end

--- Gets the channel's name. This is the lowercase login name.
---
---@return string name
function Channel:get_name() end

--- Gets the channel's type
---
---@return ChannelType
function Channel:get_type() end

--- Get the channel owner's display name. This may contain non-lowercase ascii characters.
---
---@return string name
function Channel:get_display_name() end

--- Returns true for twitch channels.
--- Compares the channel Type. Note that enum values aren't guaranteed, just
--- that they are equal to the exposed enum.
---
---@return bool
function Channel:is_twitch_channel() end

--- Sends a message to the target channel.
--- Note that this does not execute client-commands.
---
---@param message string
---@param execute_commands boolean Should commands be run on the text?
function Channel:send_message(message, execute_commands) end

--- Adds a system message client-side
---
---@param message string
function Channel:add_system_message(message) end

--- Finds a channel by name.
---
--- Misc channels are marked as Twitch:
--- - /whispers
--- - /mentions
--- - /watching
--- - /live
--- - /automod
---
---@param name string Which channel are you looking for?
---@param platform Platform Where to search for the channel?
---@return Channel?
function Channel.by_name(name, platform) end

--- Finds a channel by the Twitch user ID of its owner.
---
---@param string id ID of the owner of the channel.
---@return Channel?
function Channel.by_twitch_id(string) end

---@return string
function Channel:__tostring() end

-- Back to src/controllers/plugins/LuaAPI.hpp.

--- Registers a new command called `name` which when executed will call `handler`.
---
Expand All @@ -34,22 +129,6 @@ function c2.register_command(name, handler) end
---@param func fun(query: string, full_text_content: string, cursor_position: integer, is_first_word: boolean): CompletionList The callback to be invoked.
function c2.register_callback(type, func) end

--- Sends a message to `channel` with the specified text. Also executes commands.
---
--- **Warning**: It is possible to trigger your own Lua command with this causing a potentially infinite loop.
---
---@param channel string The name of the Twitch channel
---@param text string The text to be sent
---@return boolean ok
function c2.send_msg(channel, text) end

--- Creates a system message (gray message) and adds it to the Twitch channel specified by `channel`.
---
---@param channel string
---@param text string
---@return boolean ok
function c2.system_msg(channel, text) end

--- Writes a message to the Chatterino log.
---
---@param level LogLevel The desired level.
Expand Down
59 changes: 39 additions & 20 deletions scripts/make_luals_meta.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,42 +38,51 @@
-- Add the folder this file is in to "Lua.workspace.library".

c2 = {}

---@class IWeakResource

--- Returns true if the channel this object points to is valid.
--- If the object expired, returns false
--- If given a non-Channel object, it errors.
---@return boolean
function IWeakResource:is_valid() end

"""

repo_root = Path(__file__).parent.parent
lua_api_file = repo_root / "src" / "controllers" / "plugins" / "LuaAPI.hpp"
lua_meta = repo_root / "docs" / "plugin-meta.lua"

print("Reading from", lua_api_file.relative_to(repo_root))
print("Writing to", lua_meta.relative_to(repo_root))
with lua_api_file.open("r") as f:
lines = f.read().splitlines()

# Are we in a doc comment?
comment: bool = False

# Last `@lua@param`s seen - for @exposed generation
last_params_names: list[str] = []
# Are we in a `@lua@class` definition? - makes newlines around @lua@class and @lua@field prettier
is_class = False
def process_file(target, out):
print("Reading from", target.relative_to(repo_root))
with target.open("r") as f:
lines = f.read().splitlines()

# The name of the next enum in lua world
expose_next_enum_as: str | None = None
# Name of the current enum in c++ world, used to generate internal typenames for
current_enum_name: str | None = None
# Are we in a doc comment?
comment: bool = False

with lua_meta.open("w") as out:
out.write(BOILERPLATE[1:]) # skip the newline after triple quote
# Last `@lua@param`s seen - for @exposed generation
last_params_names: list[str] = []
# Are we in a `@lua@class` definition? - makes newlines around @lua@class and @lua@field prettier
is_class = False

for line in lines:
# The name of the next enum in lua world
expose_next_enum_as: str | None = None
# Name of the current enum in c++ world, used to generate internal typenames for
current_enum_name: str | None = None
for line_num, line in enumerate(lines):
line = line.strip()
loc = f'{target.relative_to(repo_root)}:{line_num}'
if line.startswith("enum class "):
line = line.removeprefix("enum class ")
temp = line.split(" ", 2)
current_enum_name = temp[0]
if not expose_next_enum_as:
print(
f"Skipping enum {current_enum_name}, there wasn't a @exposeenum command"
f"{loc} Skipping enum {current_enum_name}, there wasn't a @exposeenum command"
)
current_enum_name = None
continue
Expand All @@ -94,7 +103,7 @@
out.write(", ")
out.write(entry + ": " + current_enum_name)
out.write(" }\n" f"{expose_next_enum_as} = {{}}\n")
print(f"Wrote enum {expose_next_enum_as} => {current_enum_name}")
print(f"{loc} Wrote enum {expose_next_enum_as} => {current_enum_name}")
current_enum_name = None
expose_next_enum_as = None
continue
Expand All @@ -118,14 +127,19 @@
exp = line.replace("@exposed ", "", 1)
params = ", ".join(last_params_names)
out.write(f"function {exp}({params}) end\n")
print(f"Wrote function {exp}(...)")
print(f"{loc} Wrote function {exp}(...)")
last_params_names = []
elif line.startswith("@includefile "):
filename = line.replace("@includefile ", "", 1)
output.write(f"-- Now including data from src/{filename}.\n")
process_file(repo_root / 'src' / filename, output)
output.write(f'-- Back to {target.relative_to(repo_root)}.\n')
elif line.startswith("@lua"):
command = line.replace("@lua", "", 1)
if command.startswith("@param"):
last_params_names.append(command.split(" ", 2)[1])
elif command.startswith("@class"):
print(f"Writing {command}")
print(f"{loc} Writing {command}")
if is_class:
out.write("\n")
is_class = True
Expand All @@ -140,3 +154,8 @@

# note the space difference from the branch above
out.write("--- " + line + "\n")


with lua_meta.open("w") as output:
output.write(BOILERPLATE[1:]) # skip the newline after triple quote
process_file(lua_api_file, output)
2 changes: 2 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,8 @@ set(SOURCE_FILES
controllers/pings/MutedChannelModel.cpp
controllers/pings/MutedChannelModel.hpp

controllers/plugins/api/ChannelRef.cpp
controllers/plugins/api/ChannelRef.hpp
controllers/plugins/LuaAPI.cpp
controllers/plugins/LuaAPI.hpp
controllers/plugins/Plugin.cpp
Expand Down
4 changes: 4 additions & 0 deletions src/common/Channel.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ enum class TimeoutStackStyle : int {
class Channel : public std::enable_shared_from_this<Channel>
{
public:
// This is for Lua. See scripts/make_luals_meta.py
/**
* @exposeenum ChannelType
*/
enum class Type {
None,
Direct,
Expand Down
Loading
Loading