Table of Contents

Using Python in EPIC5

High-level interface to the EPIC python integration.

This module exposes functions and classes that make interfacing your python code with the EPIC irc client more pythonic.

Getting Started

Before you can load your script the environment must be initialized. We have provided a script for this purpose that works for the most common cases. You can run it by typing “/load python” at the EPIC prompt. If you wish to have the python environment initilized each time you start EPIC you can add “load python” to your `~/.epicrc` file.

Quick Examples

@alias('epic_command')
def epic_command(args):
    '''A python function working as an epic alias.
    '''
    say('Saying ' + args)
    say('Version: ' + expression('info(i)'))


@on('hook')
def on_hook(args):
    '''A python function working as an epic hook.
    '''
    say('Hooked: ' + args)

EPIC For The Python Programmer

EPIC is a fork of the ircii client <https://en.wikipedia.org/wiki/IrcII>. It has been actively developed since 1993. It is stable and mature and offers many benefits for both background (bot) usage and as an IRC client.

As a client, EPIC offers a robust and well tested TUI. The flexibility of the client allows you to customize the interface in almost every way. Through the use of script packs a user can make her client behave according to a number of different paradigms.

As a daemon, EPIC offers a robust and well tested event framework. All you need to worry about is what events you want to react to. EPIC handles the differences between IRC networks and presents them to you in a coherent way. If you run your bot inside screen or tmux you can interactively examine and interact with your code as it's running, making debugging easier.

There are three primary ways that your python code will be executed by EPIC:

User Typed Commands

Much like your shell, a command typed at the EPIC prompt has the full power of the ircii language available to it. This allows the user to build powerful constructs on their irc input line, much in the same way shells allow you to build powerful constructs:

    /fe (#epic #python) channel { join $channel }

You can create your own user-typed commands using the @alias() decorator (explained below.) You can poke around the alias system yourself by typing “/alias”, or “/alias <alias_name>” at the EPIC prompt.

Event Triggers

An event trigger (aka “on” or “hook”) is a piece of code that is executed when an event happens. There are event triggers available for any event that might happen within EPIC. Some of the more common events include connecting to a server, joining a channel, or a message being sent to the user or a channel the user has joined.

You can create your own event triggers using the @on() decorator (explained below.) You can look at the existing event triggers by typing “/on” at the EPIC prompt.

Socket Listeners

If you want to provide a network service, such as an HTTP or IRC server, you will need to register your listening socket with EPIC. You can do this with the `register_listener_callback()` function or the `SocketServerMixin` class, both explained below.

Logging and Output

EPIC configures a log handler for you. You don't have to worry about logging details inside your script, simply setup a logger like so:

    import logging

    logger = logging.getLogger('my_script')
    logger.info('Hello, World!')

Anything output to STDOUT or STDERR will automatically be captured and output into the user's EPIC window.

Classes

This module comes with one class.

SocketServerMixin

If you use a server based on Python's `socketserver.BaseRequestHandler` class you can easily hook into EPIC's callback system by using `SocketServerMixin`. Most of the included servers, such as `socketserver.TCPServer` and `http.server.HTTPServer`, work with this mixin.

Under the hood `epic.SocketServerMixin` registers a socket callback for your class and overloads `handle_request()` to integrate with EPIC's event loop.

Decorators

This module comes with two decorators.

@alias()

When a function is decorated with @alias() it will be executed when the user types the name of your alias. Anything your function returns will be ignored. If you wish to convey information back to the user you will need to use a `logger`, `echo()`, `xecho()`, or `command()`.

Alias functions will be called with exactly one argument- a string containing any arguments typed after the command.

Example:

    @alias('hello')
    def hello(args):
        xecho('Hello, %s!' % args)

When the user types “/hello Python World” it will translate to

`hello('Python World')`.

All python aliases have a small ircii shim installed. You can examine this shim by typing “/alias hello” at the EPIC prompt.

@on()

When an event happens EPIC will call any registered event handlers. Events are thrown for basically everything that happens. By the time your client has connected to a server, before it has even joined a channel, dozens of events have been thrown.

You use the @on() decorator to register your event handler. An event handler is a function that takes a single argument. When called it will be passed a string containing one or more words corresponding to the event. For now parsing that string is up to each event handler.

Example:

    @on('privmsg', '*enemy ships*', NOISE_QUIET)
    def enemy_ships(args):
        xecho("It's a trap!")

In this example anytime someone sends a message with the phrase “enemy ships” their client will print out a warning:

    *** It's a trap!

All python event triggers have a small ircii shim installed. You can examine the shim installed for our example by typing “/on privmsg” at the EPIC prompt.

Calling EPIC From Python

If you want EPIC to do something for you there are 3 primary ways to do so.

xecho()

You can use xecho() to output text to one or more of EPIC's windows. Your text is prepended with the default banner by default, but this can be overridden. There are a lot of options that control where (or even if) your text will be output. See the docstring for xecho() for more details.

Example:

    xecho("It's a trap!")

command() and evaluate()

If you want to execute a command as if the user had typed it at the EPIC prompt you can use either command() or evaluate(). The primary difference between the two is whether EPIC will parse the line and expand any variables or ircii functions it contains.

Most of the time you want to use command(), especially if you are passing in untrusted input. This will not do any parsing of the command line.

If you use evaluate() EPIC will parse the line first to replace $-strings with their expandos.

These functions will always return None.

expression()

When you need to get a piece of data from EPIC you can use expression(). EPIC expressions are roughly analogous to python expressions. Within an expression you don't use $ to indicate an expando.

Example, check to see if they have logging turned on:

    expression('LOG') == 'ON'

Example, get the channel operators for a channel:

    chanops = expression('chops(#epic)')

Example, get the list of servers you're connected to:

    servers = expression('myservers()')

register_listener_callback()

Due to the way EPIC embeds python your python code will not have its own main loop. To allow network services to work EPIC lets you register a callback for a network socket using `register_listener_callback()`. When new connections come in your callback will be called so you can handle the incoming request.

Calling Python from EPIC

There are two primary ways to call Python from within EPIC. You can execute a statement using /python or execute an expression you using $python().

/python

You can use /python to execute a python statement from the top-level (main) namespace. You can not execute an expression using /python, if you attempt to do so a stack trace will be output to the currently active window.

$python()

You can use $python() to execute a python expression and get the resulting object back. Strings will be passed as bare strings, any other object type will be passed back as a repr() string.

Helpful Aliases

There are a couple helpful aliases you can use when developing.

Everything Else

There are a lot of nooks and crannies that are outside the scope of this document. It is hoped that this gives you a good grounding for writing python code that interfaces with EPIC, but it does not talk about how to actually use EPIC. EPIC has a rich feature set which you can access from Python, please take some time to browse the full documentation here:

https://epicsol.org/

If you want to know more, or just want to chat with like-minded programmers, please come talk to us:

  Network: EFnet (irc.efnet.net)
  Channel: #epic

Acknowledgements

EPIC5 and the low-level Python functionality was written by hop.

This document and the high-level Python module was written by skullY.

A big thanks to hop for being willing to add “python support that doesn't suck”. He has spent many hours both talking to me and learning about how to embed python in his program.

Finally, thanks to caf and everyone else in #epic for providing feedback along the way. This integration is stronger for it.