Skip to content

Documentation

AWeirdDev edited this page Feb 7, 2023 · 29 revisions

Table Of Contents

Just skip to the part where you want to get started.

Topic Description
Client Represents a LINE Official Acc...
linelib.ext Extensions for building your bot.
linelib.notify The LINE Notify extension.
Resources Linelib might include some wei...

Client

class Client(
    self,
    channel_secret: str,
    channel_access_token: str,
    *args, **options: Any
)

Represents a LINE Official Account (Client).

Client - Parameters

parameter type default description
channel_secret str required Channel secret.
channel_access_token str required Channel access token.
*args, **options Any (), {} No usage provided.

Client - Example

from linelib import Client

client = Client('channel secret', 'channel access token')

Client - Attributes

  • CS : str - Channel secret.
  • CAT : str - Channel access token.
  • app : Flask - (Running) Flask application. Note that it has been used CORS(app)
  • headers : dict - Headers for requests.
  • loop : Loop- Asyncio event loop.
  • _EVENTS* : dict- Saved event handlers.
  • _VALID_EVENTS* : list - Current valid events.
๐Ÿ“ข Notice!
* - These attributes should not be overwritten or deleted by the user.

Client - Methods

Client.createEvents

def createEvents(
    self,
    name: str,
    obj: type
) -> None

Save the provided content to Client._EVENTS.

Client.createEvents - Parameters

parameter type default description
name str required Event name.
obj type required An event object.

Client.createEvents - Example

from linelib import Client

client = Client(...)

class ControlledEventObject:
    def __init__(self, json: dict):
        self.type = json['type'] # event type, REQUIRED
        self.options = json['options'] # additional options, REQUIRED
    
    async def emit(self, ctx):
        await ctx.reply('Hello, World!')

obj = ControlledEventObject({
    'type': 'text', # text message event
    'options': () # Empty
})

client.createEvents('text', obj) # registers the event handler

Client.emitEvents

def emitEvents(
    self,
    name: str,
    *args: Any,
    **kwargs: Any
) -> None

Runs the events inside Client._EVENTS.

Client.emitEvents - Parameters

parameter type default description
name str required The event name.
*args, **kwargs Any (), {} Any arguments to add for the event handlers.

Client.emitEvents - Example

from linelib import Client

client = Client(...)

@client.event('ready')
async def execute_me():
    print("I just got executed!")

client.emitEvents('ready') # no arguments required

Client.event

@event(
    self,
    listener: str,
    *options
) -> EventObject

Registers an event handler. See Client._VALID_EVENTS to see a list of valid events.

Client.event - Parameters

Decorator Parameters

parameter type default description
listener Union[str, Callable] FUNCTION_NAME_OR_LISTENER Listener name or leave blank.
๐Ÿ“– Reference
FUNCTION_NAME_OR_LISTENER - You can pass in a valid function (Client._VALID_EVENTS), or you can leave this blank and let linelib detect your function name. (Prefix: on_ + Event name)

Handler Parameters

Handlers must be async (coroutine) functions.

parameter type description
ctx? None? Depends The specification depends on the event type.

Depends: ctx? None?

event name argument(s) type description
ready โŒ โŒ No arguments should be passed.
text ctx TextMessageEvent The text message context.
postback ctx PostbackEvent The postback context.
sticker ctx StickerMessageEvent The sticker message context.
unsend ctx UnsendEvent Message unsent. (Cannot reply)
follow ctx FollowEvent A user added your bot as friend or unblocked your bot.
unfollow ctx UnfollowEvent A user blocked your bot.
join ctx JoinEvent Occurs when your bot joins a group chat.
leave ctx LeaveEvent Occurs when your bot leaves a group chat.
memberJoined ctx MemberJoinEvent Occurs when a member joins the group chat.
memberLeft ctx MemberLeaveEvent Occurs when a member leaves the group chat.
videoPlayComplete ctx VideoViewingCompleteEvent The user finishes viewing a video for at least once.
beacon ctx BeaconEvent LINE beacon event (enter, stay, banner)
accountLink ctx AccountLinkEvent The user has linked their LINE account with a provider's service account.
image ctx ImageMessageEvent The image message context. You can download them.
video ctx VideoMessageEvent The video message context. You can download them.
audio ctx AudioMessageEvent The audio message context. You can download them.
file ctx FileMessageEvent The file message context. You can download them.
location ctx LocationMessageEvent The location event context.

Client.event - Example

from linelib import Client

client = Client(...)

# method 1
@client.event('ready')
async def ready(): # any name you want
    print("WOW!")

# method 2
@client.event()
async def on_ready(): # on_{EVENT_NAME}
    print("Wow, again -- wow!!")

@client.event('text')
async def on_text(ctx):
  await ctx.reply('Good morning!')

client.run(...)

Client.load_cog

def load_cog(
    self,
    cog: type
)

Loads a Cog (extension).

Client.load_cog - Parameters

parameter type default description
cog type required The cog to load.

Client.run

def run(
    self,
    *args,
    **options: Any
)

Runs the LINE bot.

Client.run - Parameters

parameter type default description
**options Any {} Configure run options. See Client Options โ†’

โš™๏ธ Client Options

option type description example
log_level level Sets the logger level of Flask. log_level=logging.ERROR
show_logs bool Show linelib logs? show_logs=False
* Any Unnamed for linelib, only for app.run options. threaded=True

Client.run - Example

from linelib import Client

client = Client(...)

client.run(log_level=logging.ERROR, show_logs=False, threaded=True)

linelib.ext

import linelib.ext

Extensions for building your bot.

SocialPlugins

class SocialPlugins

The LINE Social Plugins. (extension)

SocialPlugins.share

@staticmethod
def share(
  url: str
) -> str

Returns a LINE Share Link. This is useful when you want a custom button.

SocialPlugins.share - Example

from linelib.ext import SocialPlugins

print(SocialPlugins.share("https://example.com"))

Depends

constructed <_depends>: Depends

Represents something depends on the specification.

Depends - Methods

__bool__

def __bool__(self) -> bool

Returns False.

__repr__

def __repr__(self) -> str

Returns "It Depends."

__hash__

def __hash__(self) -> int

Returns 0.

__getitem__

def __getitem__(self, k) -> Any

Returns k.

Depends - Example

from linelib.ext import Depends

def my_function(arg: Depends[str] = None):
  pass

commands

from linelib.ext import commands

commands module.

commands.VALID_TYPES

VALID_TYPES: list[type]

An array of valid typings for cog command arguments.

commands.Cog

class Cog(self)

Represents a command cog.

The above represents an __init__ method, which is not recommended using until you've constructed with __init_subclass__.

Useful Links
๐Ÿ“ฆ Properties โ†’
โš™๏ธ Methods โ†’
โœจ Loading Cogs โ†’

Cog.__init_subclass__

def __init_subclass__(cls) -> None

Subclass initialization (highly recommended). This gathers valid linelib cog commands and store them into _ll_COMMANDS โ†’ for further usage.

Example

from linelib.ext import commands

class MyCog(commands.Cog):
  ...

Cog.emit

async def emit(self, ctx: type) -> Depends[str]

Emits the commands inside the cog. (DO NOT TOUCH)

Returns "all-nf" (str) if every command name cannot satisfy the condition (command not found).

See Depends for typing โ†’

Cog.not_found

async def not_found(self, ctx, command: str) -> Any

Emits when a command is not found. This coroutine function is overwritable.

Example

from linelib.ext import commands

class MyCog(commands.Cog):
  async def not_found(self, ctx, command):
    await ctx.reply(f"Command '{command}' not found.")

commands.Cog - Properties

properties default description
name "UnnamedCog" The name of the cog.
show_not_found_log False Whether to show command not found log or not.
_ll_CONSTRUCTED* False Identification to check if this cog is constructed when using Client.load_cog.
_ll_COMMANDS* [] Temporary list to store commands detected inside a cog class.
๐Ÿ“ข Notice
*: Do not touch

commands.CogCommandWrapper

class CogCommandWrapper(
  self,
  cmd_name: str,
  func,
  rule: CommandRule | _dfr = DEFAULT_RULE
)

Represents a cog command.

CogCommandWrapper - Parameters

parameters type default description
cmd_name str required Command name.
func Callable required Handler function.
rule CommandRule | _dfr DEFAULT_RULE Command rules.

CogCommandWrapper.emit

async def emit(self, o: Cog, ctx: type) -> Depends[str]

Event emitting for the handler. Returns "no" (str) if this command does not equal to the context. (ctx.content)

DO NOT TOUCH

See Depends for typing โ†’

CogCommandWrapper.emit - Parameters
parameters type default description
o Cog required The Cog itself.
ctx type required The context.

CogCommandWrapper.on_error

@on_error(
  self,
  function: Callable
)

Event decorator when an error occurs.

The handler must be an async (coroutine) function.

type parameters description
@decorator * You do not need to add anything.
handler self, ctx, error error: The error message.

Example

from linelib.ext import commands

class MyCog(commands.Cog):
  @commands.cog_command() # returns CogCommandWrapper
  async def my_command(self, ctx):
    ...

  @my_command.on_error
  async def my_error_handler(self, ctx, error):
    print(error)

CogCommandWrapper.rule_reject

@rule_reject(
  self,
  function: Callable
) -> Callable

Event decorator once the rule function returned False, which represents no pass.

The handler must be an async (coroutine) function.

type parameters description
@decorator * You do not need to add anything.
handler self, ctx The context.

Example

from linelib.ext import commands, rule

clas MyCog(commands.Cog):
  @commands.cog_command(
    name="hello",
    rule=rule.CommandRule(
      rule="cooldown",
      seconds=10
    )
  ) # returns CogCommandWrapper
  async def hello(self, ctx):
    ...

  @hello.rule_reject
  async def rejected(self, ctx):
    await ctx.reply("Rejected!")

commands.cog_command

@cog_command(
  *,
  name: String@CogCommandWrapper,
  rule: CommandRule = DEFAULT_RULE
) -> CogCommandWrapper

Represents a command for cogs.

Returns: CogCommandWrapper

@decorator parameters

parameter type default description example
* -- -- -- You need to use named keywords.
name String@CogCommandWrapper required The command name. name="hello"
rule CommandRule required Command rule. See CommandRule

Handler parameters

parameter type default description
self Cog required The cog itself.
ctx TextMessageEvent required The context.
*: type str | int | float | bool (keywordOnly?) optional / assignable The command parameter for the user to pass in.

Get to know more about this kind of argument-passing condition!

Example

from linelib.ext import commands, rule

class MyCog(commands.Cog):
  @commands.cog_command(
    name="hello",
    rule=rule.CommandRule(
      rule="cooldown",
      seconds=5
    )
  async def hello(self, ctx, number: int):
    await ctx.reply(f"Your number was: {number}")

  @hello.rule_reject
  async def on_cooldown_now(self, ctx):
    await ctx.send("You are in a cooldown.")

commands.String

constructed <_str>: String

Represents any string.

commands.String - Methods
def __matmul__(self, other) -> Any

Returns other.

commands.String - Example
from typing import Any
from linelib.ext import commands

commands.String@Any

DatabaseTable

class DatabaseTable

A temporary database that helps you to store some information.

This old method is not recommended.

Example

# run_me.py

from linelib.ext import DatabaseTable as dbt

dbt.test = "My String" 
dbt.cool = True
# main.py
import run_me # runs the 'run_me.py' file
from linelib.ext import DatabaseTable as dbt

print(dbt.test) # 'My String'
print(dbt.cool) # True

rule

from linelib.ext import rule

Rules for cog commands. New in v2.2

rule.VALID_RULES

VALID_RULES: dict

Returns valid rules and the additional arguments that are required.

Returns:

# RETURN VALUE
{
    # RULE          ARGUMENTS
    "cooldown":     ('seconds',), 
    "except":       ('users',), 
    "for":          ('users',),
    "based.custom": (),
    "usage_limit":  ('times',)
}

rule.DEFAULT_RULE

constructed <_dfr>: DEFAULT_RULE

The default rule class. Always returns True. (constructed)

Definition:

class _dfr:
  def emit(self, *args, **kwargs) -> bool:
    return True

rule.CommandRule

class CommandRule(
  self,
  *,
  rule: Literal["cooldown", "except", "for", "based.custom", "usage_limit"],
  **variations
)

Represents a command rule. Example Usage

CommandRule - Parameters

parameter type default description
* -- -- Please use keyword-only arguments.
rule str required The rule type.
**variations Depends[Any] {} Additional arguments for the rule. The specification depends on the rule type.

CommandRule - Additional Arguments

Depending on the rule type, there are several additional arguments you should pass in.

rule type description argument argument description example
cooldown Command cooldown for a user. seconds: int Cooldown duration in seconds. seconds=10
except Run the command except for someone. users: list[str] A list of user IDs to prevent from using. users=['id 1', 'id 2']
for Run the command for some people. users: list[str] A list of user IDs that can use this command. users=['id 1', 'id 2']
based.custom Based on your custom rule. Nothing. See Custom Rules. --
usage_limit The user could only use the command for a limited amount of time. times: int Limited amount of time. times=10

CommandRule - Custom Rules

To build a custom rule, it requires the __init_subclass__ method.

In your custom class, note that it must contain the "handler" function, and it must NOT be a coroutine (async) function.

Your handler must return a bool (Boolean).

return value description
True The user has passed the your check function, and could use the command.
False The user didn't pass your check function, and could NOT use the command.

Example

from linelib.ext import commands, rule

class MyRule(rule.CommandRule):
  def handler(self, ctx):
    ... # your code to detect something
    return True # `True` for passing, `False` for denied!

class MyCog(commands.Cog):
  @commands.cog_command(
    name="test",
    rule=MyRule(
      rule="based.custom" # required
    )
  )
  async def test_command(self, ctx):
    ... # rest of your code here

CommandRule - Example

from linelib.ext import rule, commands

class MyCog(commands.Cog):
  @commands.cog_command(
    name="hello",
    rule=rule.CommandRule(
      rule="cooldown",
      seconds=10
    )
  )
  async def hello_cmd(self, ctx):
    await ctx.send("You're not in a cooldown")

  @hello_cmd.rule_reject
  async def rejected(self, ctx):
    await ctx.send("The cooldown is 10 seconds")

linelib.notify

import linelib.notify

The LINE Notify extension.

Notify

class Notify(
  self,
  access_token: str
)

Represents a LINE Notify bot.

Useful Links
๐Ÿ“ฆ Register your service
๐Ÿ”‘ Create a token

Parameters

parameter type default description
access_token str required Access token.

Notify.notify

async def notify(
  self,
  messages: str,
  image_thumbnail: URL = "",
  image_full_size: URL = "",
  notification_disabled: bool = False
)

Sends a message. Note that this is a coroutine function.

parameter type default description
messages str required The message content.
image_thumbnail str "" The image URL. (small size)
image_full_size str "" Full size image URL.
notification_disabled bool False Whether disable the push notification or not.

notify - Example

from linelib import Client
from linelib.notify import Notify

client = Client("channel secret", "channel access token")
notify_bot = Notify("access token")

@client.event('ready')
async def ready_event():
  await notify_bot.notify("Hello, World!")

client.run()

Resources

Oh, boy! I love resources. They are nice and warm. Yes, no doubt.

Contents

Typing

Linelib might include some weird, and difficult-to-understand typings in the code.

However, the following descriptions could help you to get along with, or even be a real chad at them.

Typing - Asterisk

Linelib has a pretty unique cog command system, inspired by the discord.py module. However, it seems difficult to understand how it works by the type definition that I mentioned here.

This is how the type looks like: *: type. The asterisk (*) represents Any, and in this condition it represents any parameters. As well as the type represents any type that is available (int, float, bool, str).

In addition, *: type means that you should add parameters with type annotations to force Linelib convert it into a specific type.

Note that it is also possible to add keyword-only arguments. Keyword-only arguments should only have one, since it represents that any content left in the message (besides previous arguments & command name mentions) will be automatically passed into this single argument. Hard to understand? Let's break it down with a simple example, assuming the command name is /hi, and the argument type annotations below:

# THIS IS NOT A VALID LINELIB CODE
# DEMONSTRATION PURPOSES ONLY
async def the_handler(ctx, arg1: int, *, arg2: str):
  print(type(arg1))
  print(arg2)

In this example (assuming this code works fine as a command cog), the user-sent messages will end up like this:

User: /hi 10 Good morning, sir!

The printed output:

<class 'int'>
Good morning, sir!

Linelib converted them with the same typing your annotation mentioned. In short, once the user send that message, the following is the result:

ctx: (The context)
arg1: 10 (int)
arg2: Good morning, sir! (str)

Let's see another example of not using the keyword-only asterisk.

async def handler_v2(ctx, arg1: int, arg2: str):
  print(type(arg1))
  print(arg2)

Message:

User: /hi 10 Good morning, sir!

The printed output:

<class 'int'>
Good

Now it's clear enough.

The argument arg2 did not return the value we expected, since we want the full sentence instead of just a single yet funny word.

In conclusion: The keyword-only asterisk (*) tells Linelib to put a full sentence into one argument, and type annotations force Linelib to convert the type of the original content (str) to the specified type.

Even Further...

Still don't understand? Feel free to take these FAQs as good advice ๐Ÿ˜Ž

1: 2 Keyword-only Arguments

Question 1: What if I add two keyword-only arguments?

It seems that it might cause a problem. However, Linelib processes the type annotations BEFORE starting your Bot application:

Program Start -> Process Arguments -> Process Type Annotations -> Raise An Error if "Multiple" keyword-only Arguments Found

In short: It is not possible to add two keyword-only arguments in this condition.

4: Custom Types

Question 2: What if I use my own type that's not on the list?

No, it's currently impossible for you to add your own types, since Linelib will raise an error which tells you to use the available types.

If you really hate the default types and would love us to add the feature, please consider submitting a feature request.

3: More Examples

Question 3: I want more examples!!

Certainly! If you hate text descriptions like I do, feel free to understand in code ๐Ÿง 

Note that the following code examples are not valid Linelib cog commands, but you get the idea! ๐Ÿ˜…

Simple Annotation
async def drink(ctx, bottles: int):
  print(f"Drank {bottles} bottles of water.")
With Keyword-only Arguments
async def say(ctx, *, text: str):
  print(f"The user said: {text}")
Default values
async def eat(ctx, *, reason: str = "No reason provided"):
  print(f"Eating, reason: {reason}")
Advanced
async def kick(ctx, times: int, *, reason: str = "No reason provided"):
  print(f"You kicked someone {times} times. Reason: {reason}")
Still don't get it?

Have another question to ask? Feel free to Ask the Community!

Hello! I am a cute sidebar that makes you to read this whole text with no doubt.

I was born in 1969 and collaborated with Gustavo Fring for my entire life, and my motto is: "Life is a career."

Actually, I have a more important announcement to make besides my profile.

My name is Sustavo Fring, but you can call me Sus.

I am the secondary owner of Los Pollos Hermanos that almost nobody knows.

Gustavo and I created an empire.

I AM THE DANGER.

My name is Walter Hartwell White. I live at-

Clone this wiki locally