keybase / pykeybasebot Goto Github PK
View Code? Open in Web Editor NEWPython Keybase Bot Library
License: BSD 3-Clause "New" or "Revised" License
Python Keybase Bot Library
License: BSD 3-Clause "New" or "Revised" License
When I run import pykeybasebot
, I get this error:
$ python3
Python 3.7.4 (v3.7.4:e09359112e, Jul 8 2019, 14:54:52)
[Clang 6.0 (clang-600.0.57)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import pykeybasebot
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/pykeybasebot/__init__.py", line 1, in <module>
from .bot import *
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/pykeybasebot/bot.py", line 8, in <module>
from .chat_client import ChatClient
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/pykeybasebot/chat_client.py", line 3, in <module>
from .types import chat1
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/pykeybasebot/types/chat1/__init__.py", line 20, in <module>
from typing_extensions import Literal
ModuleNotFoundError: No module named 'typing_extensions'
>>>
This didn't happen in version 0.1.1.
this project needs CI on travis. peek at make test
to get started. and please either add mypy to it (it currently fails) or make a new issue just to get mypy passing and added.
setting a paper key and username manually does not work. the bot will only use the currently logged in user.
this is unexpected behavior and the JS library does not do this.
further details: https://twitter.com/JuneyJune4888/status/1227645756238159872
at the very least to run the checks the precommit hooks run, unit tests would be nice.
locally i get this on precommit:
isort....................................................................Passed
black....................................................................Passed
flake8...................................................................Passed
mypy.....................................................................Failed
hookid: mypy
mypy: can't read file '**/*.py': No such file or directory
running mypy manually seems to error out:
$ poetry run mypy pykeybasebot/*.py
pykeybasebot/types/gregor1/__init__.py:18: error: Cannot find module named 'dataclasses_json'
pykeybasebot/types/keybase1/__init__.py:128: error: Cannot find module named 'dataclasses_json'
pykeybasebot/types/stellar1/__init__.py:18: error: Cannot find module named 'dataclasses_json'
pykeybasebot/types/chat1/__init__.py:25: error: Cannot find module named 'dataclasses_json'
pykeybasebot/kbevent.py:5: error: Cannot find module named 'dataclasses_json'
pykeybasebot/kbevent.py:5: note: See https://mypy.readthedocs.io/en/latest/running_mypy.html#missing-imports
pykeybasebot/chat_client.py:17: error: "ChatChannel" has no attribute "to_dict"
pykeybasebot/chat_client.py:23: error: "Type[SendRes]" has no attribute "from_dict"
pykeybasebot/chat_client.py:34: error: "ChatChannel" has no attribute "to_dict"
pykeybasebot/chat_client.py:41: error: "Type[SendRes]" has no attribute "from_dict"
pykeybasebot/chat_client.py:52: error: "ChatChannel" has no attribute "to_dict"
pykeybasebot/chat_client.py:59: error: "Type[SendRes]" has no attribute "from_dict"
pykeybasebot/chat_client.py:70: error: "ChatChannel" has no attribute "to_dict"
pykeybasebot/chat_client.py:77: error: "Type[SendRes]" has no attribute "from_dict"
pykeybasebot/chat_client.py:88: error: "ChatChannel" has no attribute "to_dict"
pykeybasebot/chat_client.py:95: error: "Type[SendRes]" has no attribute "from_dict"
pykeybasebot/cli.py:41: error: Item "None" of "Optional[StreamReader]" has no attribute "readline"
pykeybasebot/cli.py:48: error: "Type[KbEvent]" has no attribute "from_json"
would be good to catch these issues in CI before they're merged in.
I'm trying to make a keybase bot work as a restricted bot, but I'm running into issues when trying to send messages to a keybase team.
If I create a channel like this:
channel = pykeybasebot.types.chat1.ChatChannel(
name=os.environ.get("KEYBASE_TEAM"),
topic_name=os.environ.get("KEYBASE_CHANNEL"),
members_type="team",
)
Then try sending a message like this:
await bot.chat.send(channel, "test")
I get this exception:
Exception: {'code': 0, 'message': 'error from chat server: team readers are unable to create conversations, found role RESTRICTEDBOT'}
If I receive an event, I also can't respond to it using the channel that's attached to the event like this:
await bot.chat.send(event.msg.channel, "test")
It throws this exception:
Exception: {'code': 0, 'message': 'invalid send v1 options: need channel or conversation_id'}
I believe this one is because event.msg.channel
is ChatChannel(name='', public=None, members_type='team', topic_type='chat', topic_name=None)
, I think because the bot is restricted.
And it won't let me use the conv_id
as the channel either. When I receive an event and try responding like this:
await bot.chat.send(event.msg.conv_id, "test")
I get the exception:
File "/home/user/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.8/site-packages/pykeybasebot/chat_client.py", line 43, in send
"channel": channel.to_dict(),
AttributeError: 'str' object has no attribute 'to_dict'
I'm using pykeybasebot 0.2.0.
the chat_client class is missing the delete function present in other bot libraries.
your suite of tests and linters is intimidating so i wont submit a PR but i will give you the gist of the function i wrote.
https://gist.github.com/BobbyBobson4888/91d5f5087fc96041cbd2390c6ffbd480
The keybase API provides lookup:
https://keybase.io/_/api/1.0/user/lookup.json?usernames=SOMEUSER
I found Keybase class on another dead python API project & was tempted to dig into that code to see how it was done:
https://keybase-python-api.readthedocs.io/en/latest/keybase.html#the-keybase-class-accessing-public-user-data
I see no lookup on pykeybasebot so I tried extending the class & get :
ERROR:['chat api' exited with 2]
ERROR:2020-03-15T19:10:10.557769-04:00 ▶ [ERRO keybase main.go:87] 001 invalid v1 method "lookup"
Am I missing something? But an example of how to search/lookup keybase user using pykeybasebot would be greatly appreciated!
I'm trying to port my code from pykeybasebot 0.1.1 to 0.1.5 and running into a strange exception. First, I set the environment variables:
export KEYBASE_USERNAME=my_username
export KEYBASE_PAPERKEY="insert paperkey here"
Then I open a python interpreter and copy and paste this very simple script:
import os
import asyncio
import pykeybasebot
class Handler:
async def __call__(self, bot, event):
print(event)
bot = pykeybasebot.Bot(
username=os.environ.get("KEYBASE_USERNAME"),
paperkey=os.environ.get("KEYBASE_PAPERKEY"),
handler=Handler()
)
async def main():
await bot.start({
"local": False,
"wallet": False,
"dev": False,
"hide-exploding": False,
"filter_channels": None,
"filter_channel": None
})
asyncio.run(main())
I'm running this in a python:3.7.4-buster
docker container with keybase installed via the instructions at https://keybase.io/docs/the_app/install_linux. So inside the container, I'm not logged into any keybase account.
When I try running it, it throws this exception:
>>> asyncio.run(main())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/lib/python3.7/asyncio/runners.py", line 43, in run
return loop.run_until_complete(main)
File "/usr/local/lib/python3.7/asyncio/base_events.py", line 579, in run_until_complete
return future.result()
File "<stdin>", line 8, in main
File "/home/user/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.7/site-packages/pykeybasebot/bot.py", line 24, in wrapped_f
await keybase_bot_start_function(self, *args, **kwargs)
File "/home/user/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.7/site-packages/pykeybasebot/bot.py", line 87, in start
async with _botlifecycle(self, listen_options) as events:
File "/home/user/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.7/site-packages/pykeybasebot/bot.py", line 54, in __aenter__
await self.bot.ensure_initialized()
File "/home/user/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.7/site-packages/pykeybasebot/bot.py", line 113, in ensure_initialized
if not await self._is_initialized():
File "/home/user/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.7/site-packages/pykeybasebot/bot.py", line 132, in _is_initialized
f"Logged in as {actual_username} instead of {self.username}. Please logout first"
Exception: Logged in as instead of my_username. Please logout first
>>>
But I'm not logged in at all:
$ keybase status
Username:
Logged in: no
Session:
is valid: no
keys: locked
Key status:
stream: not cached
secret: not stored
dev sig: not cached
dev enc: not cached
paper sig: not cached
paper enc: not cached
prompt: show
tsec: not cached
[...snip...]
Here's my simple handler class:
class Handler:
async def __call__(self, bot, event):
if event.msg.content.type_name == chat1.MessageTypeStrings.FLIP.value:
print(event.msg.content, flush=1)
And this is what it prints out (formatting my own):
MsgContent(type_name='flip',
text=None,
attachment=None,
edit=None,
reaction=None,
delete=None,
metadata=None,
headline=None,
attachment_uploaded=None,
system=None,
send_payment=None,
request_payment=None,
unfurl=None,
flip=MsgFlipContent(
text='gqF2AaJ2MYKkYm9keYKmcmV2ZWFsgqNjY2jEIKTHVHrHQJtd85tM4MELrONjgBXuaB3PsIIdgRZcA7ddpnNlY3JldMQgduj/lmQiq2dmbcWCXJIzSjD9f6HXVb9YANbGbuC26VShdASibWSDrmNvbnZlcnNhdGlvbklExCAAAD3GsBhtlbdws7qRZMGtu622IrxZ4oI1kcznobh57qZnYW1lSUTEDHppaPupROHnFP9A8Klpbml0aWF0b3KCoWTEEH+L21NwNBEjt89tNLzmoBihdcQQlWMS5SCyN0fr1Ncg52mqAA==',
game_id='7a6968fba944e1e714ff40f0',
flip_conv_id='',
user_mentions=None,
team_mentions=None
)
)
Note that flip_conv_id
is an empty string here, thus we're unable to properly call loadflip
later. It is however available in API directly (i. e. when calling keybase chat api-listen
, it appears as intended). Any idea how to deal with that?
Having a bot advertise its available commands is super helpful. It looks like this framework doesn't support that function yet. I started looking around in the NodeJS example of how it's implemented there (Chat.prototype.advertiseCommands), but I'm not sure how to approach implementing something similar here..
In Keybase version 4.5.0-20190919193603+93e889ab01 on Ubuntu Linux 18.04, I'm getting the following error whenever a message is received by the bot user:
mashumaro.exceptions.MissingField: Field "public" of type builtins.bool is missing in pykeybasebot.kbevent.Channel instance
This error causes the bot to error out and the systemd service to restart whenever a message is received. Sent in Keybase log 52883b0197aa196367513a1c
to possibly help debug.
After following the install instructions make test
fails on the following error:
(pykeybasebot) ➜ pykeybasebot git:(master) ✗ make test
poetry run mypy pykeybasebot/
poetry run flake8
./pykeybasebot/bot.py:126:23: F541 f-string is missing placeholders
make: *** [test-static] Error 1
I wasn't sure what to pass into the logging statement, so I just passed self
and the tests worked
logging.debug(f"finished logsend {self}")
see in examples this line:
asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())
write asyncio wrapper to handle this, remove from examples. test on windows.
Hello, upon firing up the program Imports crash it...
Is there a way to get the older functioning version?
My message handler is being called multiple times for the same message.
Here's a minimal example:
import asyncio
from pykeybasebot import Bot
class Handler:
async def __call__(self, bot, event):
print("handling message")
await bot.chat.send(event.msg.conv_id, "Response")
bot = Bot(
username="yourtestbot", handler=Handler()
)
asyncio.run(bot.start({}))
Ran this and sent a message to the bot. Expected to see a single "Response" reply. Instead, I get continuous "Response" messages about 2 per second. Printing shows the message handler keeps being called for the same message.
Running keybase chat api-listen
from the command line returns the message only once, so I suspect the problem is with this library rather than the underlying api.
keybase version: keybase version 5.6.0-20200807080010+1c032f6216
pykeybasebot version: 0.2.1
there is a lot of confusion getting a bot up and running if logged in as a different user. it would be nice if the library hid these details as the js lib does.
When we setup the listen-options, then errors are always shown, because python api is expecting JSON data:
listen_options = {
"filter-channels": [
{"name": "name": "bot,myname"}
]
}
INFO:root:
DEBUG:root:executing command: ['keybase', 'chat', 'api-listen', '--filter-channels', '[{"name": "bot,myname"}]']
ERROR:root:Unable to decode JSON output: b'Message filtering is active with 1 filters\n'
ERROR:root:Unable to decode JSON output: b'filter 0: <REDACTED>\n'
ERROR:root:Unable to decode JSON output: b'Listening for chat notifications. Config: {showLocal:false showNewConvs:false hideExploding:false}, subscribeDevChannels: false\n'
I get the error below when I receive a message after I run the script.
OS: Windows 10
Python version: 3.8.0
import asyncio
import pykeybasebot
def handler(bot,event):
print(event)
bot = pykeybasebot.Bot(handler=handler, username="xxxx")
listen_options = {
"local": True,
"wallet": True,
"dev": True,
"hide-exploding": False,
"filter_channel": None,
"filter_channels": None,
}
asyncio.run(bot.start(listen_options))
ERROR:root:Unable to decode JSON output: Listening for chat notifications. Config: hideExploding: false, showLocal: true, subscribeDevChannels: true
ERROR:root:Unable to decode JSON output: Listening for wallet notifications
Traceback (most recent call last):
File ".\test.py", line 12, in <module>
bot.run()
File "C:\Users\dnorh\OneDrive\Projects\Python\OOP\keybase_bot\keybase\bot.py", line 37, in run
asyncio.run(self.bot.start(listen_options))
File "C:\Users\dnorh\AppData\Local\Programs\Python\Python38\lib\asyncio\runners.py", line 43, in run
return loop.run_until_complete(main)
File "C:\Users\dnorh\AppData\Local\Programs\Python\Python38\lib\asyncio\base_events.py", line 608, in run_until_complete
return future.result()
File "C:\Users\dnorh\AppData\Local\Programs\Python\Python38\lib\site-packages\pykeybasebot\bot.py", line 23, in wrapped_f
await keybase_bot_start_function(self, *args, **kwargs)
File "C:\Users\dnorh\AppData\Local\Programs\Python\Python38\lib\site-packages\pykeybasebot\bot.py", line 91, in start
asyncio.create_task(self.handler(self, event))
File "C:\Users\dnorh\AppData\Local\Programs\Python\Python38\lib\asyncio\tasks.py", line 382, in create_task
task = loop.create_task(coro)
File "C:\Users\dnorh\AppData\Local\Programs\Python\Python38\lib\asyncio\base_events.py", line 427, in create_task
task = tasks.Task(coro, loop=self, name=name)
TypeError: a coroutine was expected, got None
Exception ignored in: <function BaseSubprocessTransport.__del__ at 0x0000027FA991BA60>
Traceback (most recent call last):
File "C:\Users\dnorh\AppData\Local\Programs\Python\Python38\lib\asyncio\base_subprocess.py", line 126, in __del__
self.close()
File "C:\Users\dnorh\AppData\Local\Programs\Python\Python38\lib\asyncio\base_subprocess.py", line 104, in close
proto.pipe.close()
File "C:\Users\dnorh\AppData\Local\Programs\Python\Python38\lib\asyncio\proactor_events.py", line 108, in close
self._loop.call_soon(self._call_connection_lost, None)
File "C:\Users\dnorh\AppData\Local\Programs\Python\Python38\lib\asyncio\base_events.py", line 711, in call_soon
self._check_closed()
File "C:\Users\dnorh\AppData\Local\Programs\Python\Python38\lib\asyncio\base_events.py", line 504, in _check_closed
raise RuntimeError('Event loop is closed')
RuntimeError: Event loop is closed
Exception ignored in: <function _ProactorBasePipeTransport.__del__ at 0x0000027FA993D160>
Traceback (most recent call last):
File "C:\Users\dnorh\AppData\Local\Programs\Python\Python38\lib\asyncio\proactor_events.py", line 116, in __del__
self.close()
File "C:\Users\dnorh\AppData\Local\Programs\Python\Python38\lib\asyncio\proactor_events.py", line 108, in close
self._loop.call_soon(self._call_connection_lost, None)
File "C:\Users\dnorh\AppData\Local\Programs\Python\Python38\lib\asyncio\base_events.py", line 711, in call_soon
self._check_closed()
File "C:\Users\dnorh\AppData\Local\Programs\Python\Python38\lib\asyncio\base_events.py", line 504, in _check_closed
raise RuntimeError('Event loop is closed')
RuntimeError: Event loop is closed
IGNORE THIS...
Hello,
I am probably doing something unusual here with asyncio; basically my concept called for running aiohttp and pykeybasebot at the same time (I want my bot to listen to the channel and also receive webhooks) and I wasn't sure how else to do that (have tried threading, etc.) so I just throw two asyncio future tasks into the event loop.
My code is here: https://gist.github.com/ageis/b29b2565aecabe6e96e63460e26155fe it's a simple server listening on port 8080 plus sitting in a Keybase channel via your library. I've omitted the message handler, it's immaterial to the bug.
What I'm finding is that my bot successfully receives several webhooks and posts them to the channel, but then its authentication fails or something. This is being run on a headless server. So I have the systemd service running in my user session, I did keybase login
before running the script, and I also export KEYBASE_PAPERKEY
to the environment. In addition, the script has the correct paperkey hardcoded in the bot instantiation.
Any ideas why this would work initially and then throw "Login required" ?
Traceback (most recent call last):
File "/usr/local/lib/python3.7/dist-packages/aiohttp/web_protocol.py", line 418, in start
resp = await task
File "/usr/local/lib/python3.7/dist-packages/aiohttp/web_app.py", line 458, in _handle
resp = await handler(request)
File "keybase_alerts.py", line 82, in handle
await alert_bot.chat.send(channel, msg)
File "/usr/local/lib/python3.7/dist-packages/pykeybasebot/chat_client.py", line 18, in send
"message": {"body": message},
File "/usr/local/lib/python3.7/dist-packages/pykeybasebot/chat_client.py", line 98, in execute
resp = await self.bot.submit("chat api", json.dumps(command).encode("utf-8"))
File "/usr/local/lib/python3.7/dist-packages/pykeybasebot/bot.py", line 95, in submit
self.keybase_cli, command, input_data, loop=self.loop, **opts
File "/usr/local/lib/python3.7/dist-packages/pykeybasebot/cli.py", line 78, in kbsubmit
raise Exception(parsed_response["error"])
Exception: {'code': 0, 'message': 'Login required'}
Judging from the traceback, I suppose it's possible that "Login required" is my aiohttp server's reply to the client (in this case Alertmanager with a POST request), except I didn't specify basic auth anywhere so I've no clue why that would pop up. It's my first time using aiohttp incidentally. Let me know if that makes more sense; if the other library is to blame... I don't think it is though since after the first few webhooks, they continue to flow in yet aren't sent onward to the Keybase channel.
title, gives me some bullshit about event loop being closed
If i try to install pykeybasebot on python3.9, even pip doesn't argue against and installs it succesfully it looks like part of the code is not yet compatible:
kbot | Traceback (most recent call last):
kbot | File "/home/keybase/project/kbot.py", line 474, in <module>
kbot | pingbot.run()
kbot | File "/home/keybase/project/kbot.py", line 78, in run
kbot | asyncio.run(self._bot.start(listen_options))
kbot | File "/usr/local/lib/python3.9/asyncio/runners.py", line 44, in run
kbot | return loop.run_until_complete(main)
kbot | File "/usr/local/lib/python3.9/asyncio/base_events.py", line 642, in run_until_complete
kbot | return future.result()
kbot | File "/home/keybase/virtualenv/lib/python3.9/site-packages/pykeybasebot/bot.py", line 25, in wrapped_f
kbot | await keybase_bot_start_function(self, *args, **kwargs)
kbot | File "/home/keybase/virtualenv/lib/python3.9/site-packages/pykeybasebot/bot.py", line 93, in start
kbot | async for event in events:
kbot | File "/home/keybase/virtualenv/lib/python3.9/site-packages/pykeybasebot/cli.py", line 53, in kblisten
kbot | yield KbEvent.from_json(decoded_line)
kbot | File "/home/keybase/virtualenv/lib/python3.9/site-packages/dataclasses_json/api.py", line 135, in from_json
kbot | kvs = json.loads(s,
kbot | File "/usr/local/lib/python3.9/json/__init__.py", line 359, in loads
kbot | return cls(**kw).decode(s)
kbot | TypeError: __init__() got an unexpected keyword argument 'encoding'
That appears to be, because encoding keyword has been removed from python3.9:
https://docs.python.org/3/library/json.html#json.loads
This problem is already solved on new dataclasses_json versions. So, changing the version for this in pyproject.toml and also updating the rest of dependencies should do the trick.
Version with this fixed:
dataclasses-json = "^0.5.2"
This library hasn't received an update in about 4 years. There's a known Issue with this library supporting modern Python 3 versions that has had an open pull request to fix it for 3 years. I'm potentially reading too much between the lines here, but I'm just wondering if this library should maybe have a disclaimer stating that it was abandoned, if it has been? If not, maybe it could use some fresh eyes?
I'm a consumer of this library, and I'm sort of already convinced that switching to the Go library is the right call here. But if I can keep using the Python library for now, that would maybe be more ideal (for me) than rewriting the bots that are already written in a different language. Convincing myself that I shouldn't rewrite them is getting harder and harder though.
Anyway, thanks for all the work that's been put into these libraries up until the point of about 4 years ago. I'm sure the Keybase team finds maintaining the Golang library a bit easier since that's the language of the main product's backend. I actually prefer writing Golang myself, but I'm sure people who want to use Keybase and have a need for bots that don't already have a 1st party solution appreciate the array of libraries in various languages to choose from.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.