Giter VIP home page Giter VIP logo

lispcord's Introduction

Lispcord -- A wrapper for the DiscordApp WEB-API

Lispcord aims to make it freakishly easy to build bots for Discord.

The examples folder contains some more ideas on how to get started :)

NOTE: this is not even an alpha quality software

A lot of the api is changing rapidly, use at own risk!

NOTE: recently we axed the Pipe system

Please re-adjust your bots to use the new API going forwards. As it turned out, the pipes worked well for small bots but had the penchant to bloat in complexity rather quickly, and weren't particularly fast :D

Ping bot

This assumes that :lispcord has been loaded in your image. If not, try running

(ql:quickload :lispcord)

after cloning the repo to your ~/common-lisp or ~/quicklisp/local-projects folder.

(defpackage :ping-bot
  (:use :cl :lispcord))
(in-package :ping-bot)

(setf (org.shirakumo.verbose:repl-level) :info)

(defbot *ping-bot* "<Your Token Here>")
(connect *ping-bot*) ; Yes, you can register handlers after connect

(add-event-handler :on-message-create
  (lambda (msg) (if (string= (lc:content msg) "ping!") (reply msg "pong!"))))

Unlike many other libraries, lispcord is capable of running an arbitrary amount of client-instances at the same time.
Every (non-cache related) function takes an optional "bot" parameter, either via keyword or as an anonymous optional, with which you can specify what instance should execute the action.

For convenience, however, lispcord also defines a dynamic *CLIENT* which gets automatically bound to the last instance defined via DEFBOT. This allows you to:

  • drop having to specifiy the bot for 1-instance scripts
  • use LET to override the global, and create local 1-instance spaces

lispcord's People

Contributors

goose121 avatar iddm avatar jgkamat avatar lhlips avatar megaloler avatar naryl avatar s-clerc avatar sierpinskiii avatar spreadlink avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

lispcord's Issues

[question] snowflake

Only after browsing utill.lisp
i ve found out that snowflake has the following type declaration:

(deftype snowflake () '(unsigned-byte 64))

Is there any particular reason to name it so, I would rather suggest to use something similar to u64 maybe more intuitive

Channel type 15 not recognised!

I followed the simple example and got this:

Send identify for Bot *MY TOKEN*
[Event] READY
Ready payload received; Session-id: d7009ec5be0dbad0e3531eb65c63305f
Channel type 15 not recognised!
This should only happen if discord creates a new channel type and lispcord wasn't updated yet
Please file an issue at https://github.com/lispcord/lispcord/issues

Which would spam forever if I didn't disconnect, and lastly:

User: *my name*
Session: d7009ec5be0dbad0e3531eb65c63305f
Connected!
[Event] SESSIONS_REPLACE
[Event] MESSAGE_CREATE
[Event] MESSAGE_CREATE
[Event] MESSAGE_CREATE
[Event] MESSAGE_CREATE
*my name* disconnecting...

Just thought I'd share!

Make lispcord async?

There are several uses.

  1. Make it possible for a handler to wait for another gateway event.
  2. Make Discord HTTP API requests asynchronously to improve responsiveness a bit.
  3. Make it possible for user code to do any long non-lispcord operation without blocking the bot.

I propose to do it in one of the two ways:

  1. Using https://github.com/orthecreedence/cl-async, https://github.com/orthecreedence/blackbird, and https://github.com/orthecreedence/drakma-async. They are very fast but rely on a c library and make the code a bit like node.js's. I.e. Waiting on a future with lambdas for handling success and error.
  2. Using a thread pool with OS threads which may hurt performance but will provide a lispy API and not depend on C libraries.

Of course either way will increase the complexity considerably so it may be better to leave it as is. It will be a lot easier to do it in Dithcord since it wraps Lispcord's handlers feature completely. This way Lispcord remains basically a Gateway and HTTP API wrapper with little else. I'm good either way, so @spreadLink decide if you want it specifically for Lispcord.

Thread safety

I've examined the code and looks like cache is the only thing that needs to be protected. Gateway only calls handlers from its own thread, http doesn't have any shared data (AFAIS).

Some systems failed to build for Quicklisp dist

Building with SBCL 2.3.1.122-1455d60f6 / ASDF 3.3.5 for quicklisp dist creation.

Trying to build commit id 448190c

example-bot fails to build because of a failure in lispcord.

lispcord fails to build with the following error:

; caught ERROR:
;   READ error during COMPILE-FILE: Package V does not exist. Line: 70, Column: 27, File-Position: 1770 Stream: #<SB-INT:FORM-TRACKING-STREAM for "file /home/quicklisp/quicklisp-controller/dist/build-cache/lispcord/0f666cbb07e74cb35c5666bd504ff4919694230f/lispcord-20230417-git/src/util.lisp" {101264A863}>
...
Unhandled UIOP/LISP-BUILD:COMPILE-FILE-ERROR in thread #<SB-THREAD:THREAD "main thread" RUNNING {1001728003}>: COMPILE-FILE-ERROR while compiling #<CL-SOURCE-FILE "lispcord" "src" "util">

Full log here

Could we have more examples?

The library is awesome. I am learning common lisp at this moment and I like to learn something by doing real-life tasks. Now I want to code a bot for the discord. Could we have more examples? Or can we have a library API documentation so we know what to call when we need to do something? Thanks!

The value NIL is not of type STRING

I randomly got it while running a bot on several really active servers. Tell me if you need something in case I get it again. Here's the call stack from SLIME:

0: (PARSE-INTEGER NIL) [more]
  1: (LISPCORD.CLASSES::RESOLVE-CACHE #<HASH-TABLE :TEST EQL :COUNT 808 {1002775383}> #<HASH-TABLE :TEST EQUAL :COUNT 2 {1003EEE613}> :EMOJI)
  2: (LISPCORD.GATEWAY::ON-REACTION #<HASH-TABLE :TEST EQUAL :COUNT 6 {1003EED273}> #S(LISPCORD.CORE:BOT :TOKEN "MTk4NTE3NjI2MDczMjUxODUw.Xfab2w.ePkz_5HTBa2h6i4ffI7zRBUwXas" :USER #<LISPCORD.CLASSES:USER {..
  3: ((LAMBDA (EVENT-EMITTER::LISTENER) :IN EVENT-EMITTER:EMIT) #S(EVENT-EMITTER::LISTENER :FUNCTION #<CLOSURE (LAMBDA (LISPCORD.GATEWAY::MESSAGE) :IN LISPCORD.GATEWAY:CONNECT) {1005CAC1CB}> :ONCE NIL))
  4: (SB-KERNEL:%MAP-FOR-EFFECT-ARITY-1 #<CLOSURE (LAMBDA (EVENT-EMITTER::LISTENER) :IN EVENT-EMITTER:EMIT) {1003EEC94B}> #(#S(EVENT-EMITTER::LISTENER :FUNCTION #<CLOSURE (LAMBDA # :IN LISPCORD.GATEWAY:CON..
  5: (EVENT-EMITTER:EMIT :MESSAGE #<WEBSOCKET-DRIVER.WS.CLIENT:CLIENT {1005388BF3}> "{"t":"MESSAGE_REACTION_ADD","s":20,"op":0,"d":{"user_id":"450447741483483147","message_id":"65582971256..
  6: ((LAMBDA (FAST-WEBSOCKET::PAYLOAD &KEY :START :END) :IN FAST-WEBSOCKET::MAKE-PAYLOAD-CALLBACK) #<unavailable argument> :START #<unavailable argument> :END #<unavailable argument>)
  7: ((LABELS FAST-WEBSOCKET.PARSER::PARSER :IN FAST-WEBSOCKET.PARSER:MAKE-LL-PARSER) #(129 126 2 13 123 34 ...) :START #<unavailable argument> :END #<unavailable argument>)
  8: ((LAMBDA (FAST-WEBSOCKET::DATA &KEY :START :END) :IN FAST-WEBSOCKET:MAKE-PARSER) #(129 126 2 13 123 34 ...) :START NIL :END NIL)
  9: ((FLET "WITHOUT-INTERRUPTS-BODY-29" :IN SB-THREAD::CALL-WITH-RECURSIVE-LOCK))
 10: (SB-THREAD::CALL-WITH-RECURSIVE-LOCK #<CLOSURE (FLET SB-THREAD::WITH-RECURSIVE-LOCK-THUNK :IN WEBSOCKET-DRIVER.WS.BASE:PARSE) {7F154F0B669B}> #<SB-THREAD:MUTEX "Anonymous recursive lock" owner: #<SB-T..
 11: ((:METHOD WEBSOCKET-DRIVER.WS.BASE:PARSE (T T)) #<WEBSOCKET-DRIVER.WS.CLIENT:CLIENT {1005388BF3}> #(129 126 2 13 123 34 ...) :START NIL :END NIL) [fast-method]

RESOLVE-CACHE's TABLE actually had "id" = NIL.

Hangs on CTRL+C, can't kill

When depipe was merged, the sbcl interpreter can't be killed via ctrl-c/ctrl-d. The latest output is:

Cache-hit: 229285649721589760   :: CHANNEL
Cache-hit: 246986189410795521   :: CHANNEL
[Event] TYPING_START
[Payload] #<HASH-TABLE :TEST EQUAL :COUNT 4 {10062AA463}>
[Event] MESSAGE_CREATE
[Payload] #<HASH-TABLE :TEST EQUAL :COUNT 4 {10062AC113}>
Cache-hit: 183254040266670080   :: USER

debugger invoked on a TYPE-ERROR in thread
#<THREAD "websocket client read thread" RUNNING {1008B275C3}>:
  The value #<LISPCORD.CLASSES:MESSAGE {10070AB223}>
  is not of type
    (OR (VECTOR CHARACTER) (VECTOR NIL) BASE-STRING SYMBOL CHARACTER).
^C
debugger invoked on a SB-SYS:INTERACTIVE-INTERRUPT in thread
#<THREAD "main thread" RUNNING {100399C783}>:
  Interactive interrupt at #x7FFFF71C474D.

Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.

restarts (invokable by number or by possibly-abbreviated name):
  0: [CONTINUE] Return from SB-UNIX:SIGINT.
  1: [ABORT   ] Exit debugger, returning to top level.

("bogus stack frame")
0] ^C

debugger invoked on a SB-SYS:INTERACTIVE-INTERRUPT in thread
#<THREAD "main thread" RUNNING {100399C783}>:
  Interactive interrupt at #x7FFFF71C474D.

Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.

restarts (invokable by number or by possibly-abbreviated name):
  0: [CONTINUE] Return from SB-UNIX:SIGINT.
  1: [ABORT   ] Reduce debugger level (to debug level 1).
  2:            Return from SB-UNIX:SIGINT.
  3:            Exit debugger, returning to top level.

("bogus stack frame")
0[2] 
0] 
* Websocket closed with code: NIL
Reason: NIL

Is this repo maintained?

@spreadLink @MegaLoler Hello. I need Lispcord for a project and I may need to make a few patches. Do you still accept them (I see a PR by jgkamat just left without a comment for more than two months)?

Otherwise, with your approval, I can start calling my fork the main one and publish it to Quicklisp too.

Many type errors

Hi I just tried to use lispcord (the example) and I'm experiencing a lot of type errors.

Immediately when the client tries to connect:

The value #(), derived from the initarg :ROLES, can not be used to set the value of the slot LISPCORD.CLASSES:ROLES in #<LISPCORD.CLASSES:EMOJI #x3021ACC5EB1D>, because it is not of type (VECTOR (UNSIGNED-BYTE 64)).

when a message is sent on the server

The value "755133063729512448", derived from the initarg :NONCE, can not be used to set the value of the slot LISPCORD.CLASSES:NONCE in #<LISPCORD.CLASSES:MESSAGE #x302003AD53CD>, because it is not of type (OR NULL (UNSIGNED-BYTE 64)).

I'm not sure what I'm doing wrong, to be honest. I was worried it might be because I was using the QL version, but I'm experiencing the same issue with the downloaded version.

Can't modify individual attributes of guild member

The wrapper for the Modify Guild Member endpoint, i.e. (edit (m member) (g guild)), doesn't seem to allow any way to leave out any of the keys from the request. This is a problem because the bot may have permission to change some, but not necessarily all, of the attributes; for instance, it may have permission to change another user's nickname, but not to assign roles.

As some of the relevant slots are booleans, just setting them to NIL to exclude them won't work; however, including a slot iff it is SLOT-BOUNDP seems like it could be an elegant solution to the issue, as the client can simply use MAKE-INSTANCE to create an appropriate instance of the MEMBER class, leaving out the fields which they don't want to affect. If this is a viable solution, I'd be happy to implement it and send a pull request.

Is there a way to get mention string?

As as user I would like to have methods for mentioning stuff like users, channels, guilds, and so on (everything what could be mentioned) so that I have a very simple API for this which simply returns string. Could we have such methods?

Travis CI

My previous commit left the repo in a non-buildable state. Of course I'll try to avoid it in the future but Travis CI integration will help catch these issues a lot faster. I can set it up myself, just need the approval of the organization owner to give it read access to the repo.

Formatting

Why does github hate me :(

Seriously tho, it keeps changing double-spaces to tabs, someone pls help me

Can't get reactions to a message

Using lc:reactions always returns an empty array, even if the message has reactions.

Looking at the code, I thought that there might just be a typo on line 176 of classes/message.lisp. If I change the (gethash "embeds" table) to (gethash "reactions" table), it works and I get the expected array of reactions back. But, it only works if the message has no reactions or only reactions with custom emoji.

If the message has stock emoji, the resolve-cache function raises an error:

The value
  NIL
is not of type
  STRING
   [Condition of type TYPE-ERROR]

*snip*

Backtrace:
  0: (LISPCORD.CLASSES::RESOLVE-CACHE #S(LISPCORD.CLASSES:CACHE :DATA #<HASH-TABLE :TEST EQL :COUNT 2 {10056AD943}> :LOCK #<SB-THREAD:MUTEX "LISPCORD.CLASSES cache" (free)>) #<HASH-TABLE :TEST EQUAL :COUNT..
  1: ((:METHOD LISPCORD.CLASSES:FROM-JSON ((EQL :REACTION) HASH-TABLE)) #<unused argument> #<HASH-TABLE :TEST EQUAL :COUNT 3 {1012B9AE53}>) [fast-method]
  2: ((FLET SB-IMPL::F :IN SB-IMPL::VECTOR-MAP-INTO/SIMPLE-VECTOR) #<HASH-TABLE :TEST EQUAL :COUNT 3 {1012B9AE53}>)
  3: (SB-KERNEL:%MAP-FOR-EFFECT-ARITY-1 #<CLOSURE (FLET SB-IMPL::F :IN SB-IMPL::VECTOR-MAP-INTO/SIMPLE-VECTOR) {7FE83F95DC6B}> (#<HASH-TABLE :TEST EQUAL :COUNT 3 {1012B9A373}> #<HASH-TABLE :TEST EQUAL :COU..
  4: (SB-KERNEL:%MAP NIL #<CLOSURE (FLET SB-IMPL::F :IN SB-IMPL::VECTOR-MAP-INTO/SIMPLE-VECTOR) {7FE83F95DC6B}> (#<HASH-TABLE :TEST EQUAL :COUNT 3 {1012B9A373}> #<HASH-TABLE :TEST EQUAL :COUNT 3 {1012B9A8F..
  5: (SB-IMPL::VECTOR-MAP-INTO/SIMPLE-VECTOR #(#<LISPCORD.CLASSES:REACTION {1012B9C4D3}> #<LISPCORD.CLASSES:REACTION {1012B9CCB3}> 0) 0 3 #<CLOSURE (LAMBDA (&REST ALEXANDRIA::MORE) :IN ALEXANDRIA:CURRY) {1..
  6: (LISPCORD.UTIL:MAPVEC #<CLOSURE (LAMBDA (&REST ALEXANDRIA::MORE) :IN ALEXANDRIA:CURRY) {1012B9BCCB}> (#<HASH-TABLE :TEST EQUAL :COUNT 3 {1012B9A373}> #<HASH-TABLE :TEST EQUAL :COUNT 3 {1012B9A8F3}> #<..
  7: ((:METHOD LISPCORD.CLASSES:FROM-JSON ((EQL :MESSAGE) HASH-TABLE)) #<unused argument> #<HASH-TABLE :TEST EQUAL :COUNT 16 {1012B990C3}>) [fast-method]

How do I run the bot in example-bot?

I'm using SBCL 2.2.9.debian and I'm really new to the whole asd thing, I tried:

sbcl --load "example-bot.asd"

* (ql:quickload :lispcord) 
* (ql:quickload :example-bot)
* (example-bot:main)

debugger invoked on a SB-INT:SIMPLE-READER-PACKAGE-ERROR in thread
#<THREAD "main thread" RUNNING {1001368073}>:
  The symbol "MAIN" is not external in the EXAMPLE-BOT package.

    Stream: #<SYNONYM-STREAM :SYMBOL SB-SYS:*STDIN* {100001B4F3}>

Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.

restarts (invokable by number or by possibly-abbreviated name):
  0: [CONTINUE] Use symbol anyway.
  1: [ABORT   ] Exit debugger, returning to top level.

(SB-IMPL::READ-TOKEN #<SYNONYM-STREAM :SYMBOL SB-SYS:*STDIN* {100001B4F3}> #\e)

I'm not sure what I'm supposed to run and I'm not really getting lucky with searching related things up.
I am sorry if this isn't the right place or it is something really obvious

example-bot function common-lisp:nil is undefined

This is SBCL 2.2.9.debian, an implementation of ANSI Common Lisp.
More information about SBCL is available at <http://www.sbcl.org/>.

SBCL is free software, provided as is, with absolutely no warranty.
It is mostly in the public domain; some portions are provided under
BSD-style licenses.  See the CREDITS and COPYING files in the
distribution for more information.
* (ql:quickload :darcy)
To load "darcy":
  Load 1 ASDF system:
    darcy
; Loading "darcy"
....................
(:DARCY)
* (in-package :darcy)
#<PACKAGE "DARCY">
* (main)
#(#S(EVENT-EMITTER::LISTENER
     :FUNCTION #<FUNCTION (LABELS LISPCORD.GATEWAY::CLOSE-HANDLER :IN CONNECT) {10049EA75B}>
     :ONCE NIL))
* Initiating heartbeat every 41.25 seconds
Send identify for Bot (TOKEN)
[Event] READY
Ready payload received; Session-id: (SESSION-ID)
User: darcy
Session: (SESSION)
Connected!
[Event] GUILD_CREATE
[Event] TYPING_START
[Event] MESSAGE_CREATE

I mention the bot with the message "blah" which isn't a real command.

debugger invoked on a UNDEFINED-FUNCTION @52A00674 in thread
#<THREAD "websocket client read thread" RUNNING {10061980A3}>:
  The function COMMON-LISP:NIL is undefined.

The current thread is not at the foreground,
SB-THREAD:RELEASE-FOREGROUND has to be called in #<SB-THREAD:THREAD "main thread" RUNNING {1001358073}>
for this thread to enter the debugger.
Discord didn't reply to heartbeat. Reconnecting.
Performing full reconnect! Bot: darcy
darcy disconnecting...
Initiating heartbeat every 41.25 seconds
Send identify for Bot (TOKEN)
Websocket closed with code: NIL Reason: NIL
Attempting to reconnect! Bot: darcy
Initiating heartbeat every 41.25 seconds
Send identify for Bot (TOKEN)
[Event] READY
Ready payload received; Session-id: (SESSION-ID)
User: darcy
Session: (SESSION)
Connected!
[Event] GUILD_CREATE

Make it possible to auth as a user account

I've checked discord.py and it actually is both in the official list here:
https://discordapp.com/developers/docs/topics/community-resources#libraries-discord-libraries

And has the user account authentication feature:
https://github.com/Rapptz/discord.py/blob/e91675291e5b5a4697f869a7a01c96aa5fa3ffea/discord/client.py#L568
https://github.com/Rapptz/discord.py/blob/e91675291e5b5a4697f869a7a01c96aa5fa3ffea/discord/http.py#L127

I guess it's ok as long as you avoid words like "selfbot" :D I can do it exactly as discord.py does it. I.e.

(make-bot *token* :bot nil)

Then it would probably make sense to rename bot to client as in discord.py too. So:

;; Default usage
(make-client *token*)
;; User account usage
(make-client *token* :bot nil)

Some systems failed to build for Quicklisp dist

Building with SBCL 2.3.3.83-562a1a329 / ASDF 3.3.5 for quicklisp dist creation.

Trying to build commit id 0460017

example-bot fails to build with the following error:

Unhandled SB-KERNEL:SIMPLE-PACKAGE-ERROR in thread #<SB-THREAD:THREAD tid=541389 "main thread" RUNNING {1001738003}>: The name "VERBOSE" does not designate any package.

lispcord fails to build with the following error:

Unhandled SB-KERNEL:SIMPLE-PACKAGE-ERROR in thread #<SB-THREAD:THREAD tid=541394 "main thread" RUNNING {1001738003}>: The name "VERBOSE" does not designate any package.

Full log here

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.