webexcommunity / webexpythonsdk Goto Github PK
View Code? Open in Web Editor NEWWork with the Webex Teams APIs in native Python!
Home Page: https://webexteamssdk.readthedocs.io
License: MIT License
Work with the Webex Teams APIs in native Python!
Home Page: https://webexteamssdk.readthedocs.io
License: MIT License
If receiving a 400 response when posting a message, the following Traceback is generated:
Traceback (most recent call last):
File "/etc/icinga2/scripts/spark-host-notification.py", line 18, in
message = api.messages.create(room_id, text=message_text)
File "/var/spool/icinga2/.local/lib/python2.6/site-packages/ciscosparkapi/api/messages.py", line 282, in create
json_obj = self.session.post('messages', json=post_data)
File "/var/spool/icinga2/.local/lib/python2.6/site-packages/ciscosparkapi/restsession.py", line 188, in post
check_response_code(response, erc)
File "/var/spool/icinga2/.local/lib/python2.6/site-packages/ciscosparkapi/helper.py", line 104, in check_response_code
response=response)
File "/var/spool/icinga2/.local/lib/python2.6/site-packages/ciscosparkapi/exceptions.py", line 49, in init
response_text)
ValueError: zero length field name in format
Goal here is to provide a directory with unit test for Spark API calls supported by the library.
When listing all the rooms of a user it stops at
room name: "Domás"
When replacing all accents in the room.title it processes the above room but stops at:
room name: "Rob Ørnebånd"
So I tried to use the example from the Readme.md file, using the try-except:
except SparkApiError as e:
print(e)
Error message: "SparkApiError undefined" or something similar.
Question:
#1 .. How to deal with strange characters in room names?
(I just ran into room names starting with an emoticon)
#2 .. How to handle errors? (or see what happened when requesting a room list?)
Python: 3.5
Listing the rooms works perfectly when running the script from the command line instead of using CGI.
It is a good practice not to embedd secretes in code.
Cloud practices (see 12factor.net) are to read from an env variable.
I suggest we read by default from SPARK_TOKEN env variable.
Moreover, any sample we provide will leverage the env variable approach.
Something is broken in ciscosparkapi.exceptions.SparkApiError.__str__()
>>> spark.memberships.create(room.id, personId=me.id)
Traceback (most recent call last):
File "<input>", line 1, in <module>
File "/Users/chrlunsf/dev/projects/ciscosparkapi/ciscosparkapi/api/memberships.py", line 219, in create
json_obj = self._session.post('memberships', json=post_data)
File "/Users/chrlunsf/dev/projects/ciscosparkapi/ciscosparkapi/restsession.py", line 425, in post
**kwargs)
File "/Users/chrlunsf/dev/projects/ciscosparkapi/ciscosparkapi/restsession.py", line 276, in request
check_response_code(response, erc)
File "/Users/chrlunsf/dev/projects/ciscosparkapi/ciscosparkapi/utils.py", line 106, in check_response_code
raise SparkApiError(response)
ciscosparkapi.exceptions.SparkApiError: <unprintable SparkApiError object>
The SDK is not honoring the max
parameter in rooms.list()
.
>>> rooms = [r.id for r in sdk.rooms.list(type="direct", max=10)]
>>> len(rooms)
247
>>> rooms = [r.id for r in sdk.rooms.list(type="direct")]
>>> len(rooms)
247
The issue is that the API returns a next link in the response header even after the max
number of items have been retrieved because the max
is a "max per request" not a "max number of items to return." The SDK will need to modified to check if max
was passed in and stop getting items after max
items have already been retrieved.
Add Python logging support to the package to assist users and contributors with debugging issues.
I would like to see support of Cisco Spark event API endpoint to be added.
When working with the WebEx APIs, I would like to be able to specify what type/class of data objects I want returned for each of the API response object types. I might want the default objects returned, or I might want dictionary objects, or even my own ORM-mapped objects.
While installing ciscosparkapi, faced the following error during installation -
Have attached the full error as shown below.
Collecting ciscosparkapi
Using cached ciscosparkapi-0.9.2.tar.gz
Complete output from command python setup.py egg_info:
Traceback (most recent call last):
File "", line 1, in
File "/var/volatile/tmp/pip-build-mL2umY/ciscosparkapi/setup.py", line 62, in
packages=find_packages(include=['ciscosparkapi', 'ciscosparkapi.*']),
TypeError: find_packages() got an unexpected keyword argument 'include'
Command "python setup.py egg_info" failed with error code 1 in /var/volatile/tmp/pip-build-mL2umY/ciscosparkapi/
Please let me know if there is something obvious that I'm missing...
How do I send file attachment (not file url) using webexteams
sdk. The simple example doesn’t have an example of it.
New Admin APIs & Methods:
Select a tool that leverages the docstrings in the package's code to assist with building a live and complete set of package documentation.
Some possible tool options are available here.
Do others have experience or preference with or for any others?
The access_token.get method allows you to convert an OAUTH token into an access token for making API commands on a user's behalf for WebexTeams Integrations. However, currently the ciscospark package won't let you call this method unless you have already an API object, which is a problem since you need a token to make an API object even though you don't need a token to make this call!
Luckily, the constructor doesn't actually check if the token you give it is valid on creation. As a workaround, I currently initialise an API object with a fake token, use it to convert the OAUTH code into the new token and then use that new token to create another API object I'll use elsewhere. To demonstrate:
def makeAPI(client_id, client_secret, OAuthcode, redirect_uri):
fakeAPI = CiscoSparkAPI(access_token='blahblahblah')
tokenObject = fakeAPI.access_tokens.get(client_id, client_secret, OAuthcode, redirect_uri)
realAPI = CiscoSparkAPI(access_token=tokenObject.access_token)
return realAPI
Ideally, I should be able to initialise the API by calling API = CiscoSparkAPI(client_id, client_secret, OAuthcode, redirect_uri)
or something like that directly.
Since the official developer page for Cisco Webex doesn't have the SDK for Python, this repo is the de facto. Would the recent change of Cisco Spark to Webex Teams change anything here?
Rasa (https://rasa.com/) is an open source conversational AI platform. I am developing a Webex Teams connector for Rasa. The goal is essentially making Webex Teams as another supported input/output channel for Rasa. Rasa already has connectors for Slack, Facebook etc.
Using webexteamssdk
and following the code for Rasa Slack connector, I wrote below code so far. Wanted to make sure I am in the right direction. Thoughts?
from webexteamssdk import WebexTeamsAPI
class CiscoWebexTeamsBot(WebexTeamsAPI, OutputChannel):
"""A Cisco Webex Teams communication channel"""
@classmethod
def name(cls):
return "Cisco Webex Teams"
def __init__(self, access_token, ciscowebexteams_room=None):
self.ciscowebexteams_room = ciscowebexteams_room
super(CiscoWebexTeamsBot, self).__init__(access_token)
def send_text_message(self, recipient_id, message):
recipient = self.ciscowebexteams_room or recipient_id
for message_part in message.split("\n\n"):
super(CiscoWebexTeamsBot, self).messages.create(roomId=recipient,text=message_part)
Update Team API returning 405 response code due to the "POST" method used instead of "PUT"
some example will help newcomers start with the sdk,
don't need a lot to start with
From a user standpoint, I would actually expect a list, not generator here.
https://github.com/CiscoDevNet/ciscosparkapi/blob/master/ciscosparkapi/api/webhooks.py#L124
Traceback (most recent call last):
for member in api.memberships.list(roomId=room.id):
File "/usr/local/lib/python3.6/site-packages/ciscosparkapi/api/memberships.py", line 185, in list
for item in items:
File "/usr/local/lib/python3.6/site-packages/ciscosparkapi/restsession.py", line 390, in get_items
for json_page in pages:
File "/usr/local/lib/python3.6/site-packages/ciscosparkapi/restsession.py", line 345, in get_pages
response = self.request('GET', url, erc, params=params, **kwargs)
File "/usr/local/lib/python3.6/site-packages/ciscosparkapi/restsession.py", line 287, in request
time.sleep(e.retry_after)
TypeError: an integer is required (got type str)
CiscoSparkAPI.team_memberships.update() is using the wrong method. Should be using put instead of post
in teammemberships.py, line 239 should be changed from:
json_obj = self._session.post('team/memberships/' + membershipId,
to:
json_obj = self._session.put('team/memberships/' + membershipId,
Existing README.md (markdown) displays poorly on PyPI. Need to convert to reStructuredText.
Additionally, it would be good to revise the README's structure to cut strait to the point in demonstrating the value of the package:
Proposed README structure:
This error is intermittent and it seems it pops up when the file was not successfully attached to the message by the webex teams backend. I can't reproduce it consistently, but I encountered this problem already twice.
File ".../.venv/lib/python3.6/site-packages/webexteamssdk/api/messages.py", line 210, in create
data=multipart_data)
File ".../.venv/lib/python3.6/site-packages/webexteamssdk/restsession.py", line 393, in post
**kwargs)
File ".../.venv/lib/python3.6/site-packages/webexteamssdk/restsession.py", line 250, in request
check_response_code(response, erc)
File ".../.venv/lib/python3.6/site-packages/webexteamssdk/utils.py", line 217, in check_response_code
raise ApiError(response)
File ".../.venv/lib/python3.6/site-packages/webexteamssdk/exceptions.py", line 152, in __init__
detail = _response_to_string(response)
File ".../.venv/lib/python3.6/site-packages/webexteamssdk/exceptions.py", line 91, in _response_to_string
if request.body else "")
File ".../.venv/lib/python3.6/site-packages/webexteamssdk/exceptions.py", line 45, in _to_unicode
assert isinstance(string, basestring)
AssertionError
I was sending a message using
api.messages.create(roomId="id", markdown="text", files=["path/to/file1"])
Prefix modules and class names with a leading _
(underscore) to clearly and consistently indicate the intended user interface(s).
I am using the module on IOS XE Guest Shell with python2 only.
This API call fails, because __qualname__
is available only in Python3.
>>> spark_api.rooms.list()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python2.7/site-packages/ciscosparkapi/generator_containers.py", line 70, in __repr__
func_name=self.generator_function.__qualname__,
AttributeError: 'function' object has no attribute '__qualname__'
def update(self, webhookId, **update_attributes):
tried packaging with json.dumps and passing that way and still fails. devleoper.ciscospark.com requires webhook name and targetUrl at minimum....
I've noticed a few things in the documentation that I'd like to change. I'll submit a PR for this shortly:
I'll also see if I can fix the formatting of docstrings in the API docs. For example, the Returns
for RoomsAPI wraps over a line causing some strange output.
Hi! Thank you for webexteamssdk. The demo app works great. Does webexteamssdk
allow to make audio and video calls? Thanks!
Review modules to ensure every module, function and method is documented with a complete docstring.
For Package & Modules - DocStrings should...
For Classes, Functions & Methods - DocStrings should...
All DocStrings should use the Imperative Tense or Mood, and should use package consistent formatting (intent is to use an auto-generation tool to assist with package documentation).
more a feature request...
Here is how I had to implement in my legacy library using toolbelt... (https://pypi.python.org/pypi/requests-toolbelt)
from requests_toolbelt.multipart.encoder import MultipartEncoder
def post_localfile(at, roomId, filename, text='', toPersonId='', toPersonEmail=''):
openfile = open(filename, 'rb')
filename = ntpath.basename(filename)
payload = {'roomId': roomId, 'files': (filename, openfile, 'image/jpg')}
if text:
payload['text'] = text
if toPersonId:
payload['toPersonId'] = toPersonId
if toPersonEmail:
payload['toPersonEmail'] = toPersonEmail
m = MultipartEncoder(fields=payload)
headers = {'Authorization': _fix_at(at), 'Content-Type': m.content_type}
resp = requests.request("POST",url=_url('/messages'), data=m, headers=headers)
file_dict = json.loads(resp.text)
file_dict['statuscode'] = str(resp.status_code)
return file_dict
Hi,
It would be nice if the documentation would list which fields are returned.
Example: Memberships: http://ciscosparkapi.readthedocs.io/en/latest/user/api.html#memberships
Returns:
When iterated, the GeneratorContainer, yields
the memberships returned from the Cisco Spark query.
Nice would be the format that's used for the Parameters section , looking a bit like this
Returns:
When iterated, the GeneratorContainer, yields
the memberships returned from the Cisco Spark query.
- id
- roomId
- personId
- personEmail
- isModerator
- isMonitor
- created
Or like this:
- id, roomId, personId, personEmail, isModerator, isMonitor, created
api.people.list should allow orgId to be passed as a valid parameter to allow partner admins to scan customer orgs. Other calls may also need to be updated but I haven't checked.
There are some instances where getting all of the messages in a room results in a 404. This is a request to have the SDK handle this scenario.
Scenario:
msgs = sdk.messages.list()
msgs = [m for m in msgs]
Results in:
File "/python/lib/python3.7/site-packages/webexteamssdk/api/messages.py", line 134, in list
for item in items:
File "/python/lib/python3.7/site-packages/webexteamssdk/restsession.py", line 357, in get_items
for json_page in pages:
File "/python/lib/python3.7/site-packages/webexteamssdk/restsession.py", line 327, in get_pages
response = self.request('GET', next_url, erc, **kwargs)
File "/python/lib/python3.7/site-packages/webexteamssdk/restsession.py", line 250, in request
check_response_code(response, erc)
File "/python/lib/python3.7/site-packages/webexteamssdk/utils.py", line 213, in check_response_code
raise ApiError(response)
webexteamssdk.exceptions.ApiError: Response Code [404] Not Found - The URI requested is invalid or the resource requested, such as a user, does not exist. Also returned when the requested format is not supported by therequested method.
------------------------------------Request------------------------------------
GET https://api.ciscospark.com/v1/messages?roomId=<redacted>&max=50&beforeMessage=Y2lzY2
9zcGFyazovL3VzL01FU1NBR0UvMmVhOTVlODAtNDY0Yy0xMWU3LTgyNjctYmRlMTVmOTlmNjE5
User-Agent: python-requests/2.19.1
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Authorization: Bearer <redacted>
Content-type: application/json;charset=utf-8
-----------------------------------Response------------------------------------
404 Not Found
TrackingID: <redacted>
Cache-Control: no-cache
Content-Type: application/json
Date: Wed, 09 Jan 2019 21:39:30 GMT
Server: Redacted
l5d-success-class: 1.0
Via: 1.1 linkerd
content-encoding: gzip
content-length: 135
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
{"message":"message not found","errors":[{"description":"message not
found"}],"trackingId":"<redacted>"}
Quoting Chris:
When paging through messages in a room, eventually one of the requests will fail (with a 404 Not Found error) - even though the message referenced in the request URL is valid and does exist.
If this is the case, it sounds like Nick Mueller’s work around is to go back to the last successful request and replay it using a different page size (max<previous) such that the offending messageId won’t be requested again (it will simply be included in a page of responses- requested with a different offset).
Workaround:
Send a new message to the room and call messages.list()
again. Keep doing this until you don't hit the 404.
~/ciscosparkapiexamples $ ls /usr/local/lib/python2.7/dist-packages/ciscosparkapi/
exceptions.py helper.pyc restsession.py sparkdata.pyc
exceptions.pyc __init__.py restsession.pyc _version.py
helper.py __init__.pyc sparkdata.py _version.pyc
So when I run your example it can't load ......
~/ciscosparkapiexamples $ python example.py Traceback (most recent call last):
File "example.py", line 1, in <module>
from ciscosparkapi import CiscoSparkAPI
File "/usr/local/lib/python2.7/dist-packages/ciscosparkapi/__init__.py", line 8, in <module>
from api.accesstokens import AccessToken, AccessTokensAPI
ImportError: No module named api.accesstokens
Need to do the following here:
I put together a quick PoC of an async bot for Python and Cisco Spark.
https://github.com/tonybaloney/cisco-spark-async-bot
Happy to contribute this upstream, for bots async support get's really important when looking at multiple rooms, chats and discussions. Also, being able to use async callbacks is the difference between a fast bot and a sloow one :)
Thoughts? Does this go in another package? Would it be better to have seperate functions or a method wrapper, so that it can keep backward compat for non-Python 3 people.
Just as an opportunity to provide feedback based on my experience with your previous cmlCiscoSparkSDK package was that versioneer was an unnecessary hurdle. The versioneer version determination would fail (0_unknown) or die depending on how I tried to install the package and the Py/Pip version being used. To be honest I had more issues installing that package due to versioneer than I had using the package. The value of using versioneer for what I expect will be a low touch package doesn't seem warranted IMHO if you want users to experience an easy an simple intro to Spark.
The versioneer issues I am referring to are documented here (https://github.com/warner/python-versioneer) under the section known limitations.
My AU$0.02!
I will assign this one to myself!
In rebranding the Home Assistant Webex Teams integration, I decided to enhance it a little by posting the messages using the html
parameter. E,g,
from webexteamssdk import WebexTeamsAPI
client = WebexTeamsAPI(access_token=token)
message = "<strong>Build 0.89.6 is unstable.</strong>"
client.messages.create(roomId=room, html=message)
This gets powerful when you know which html tags are supported.
Here is the message format for those:
- alias: "Plain Test no HTML"
trigger:
- platform: webhook
webhook_id: new_version
action:
service: notify.cisco_webex_teams_notify
data:
message: "This is a pretty plain old message."
- alias: "Notify On Build Failing"
trigger:
- platform: webhook
webhook_id: build_failed
action:
service: notify.cisco_webex_teams_notify
data:
message: "<blockquote class=danger>Build 0.89.5 compile failed."
- alias: "Notify On Build Unstable"
trigger:
- platform: webhook
webhook_id: build_unstable
action:
service: notify.cisco_webex_teams_notify
data:
title: "<strong>Build 0.89.6 is unstable.</strong>"
message: "<blockquote class=warning>Version 0.89.6 failed verifications.
<ul>
<li> test_osx
<li> test_win_lint
<li>... and 4 more.
</ul>
<p><@personEmail:[email protected]></p>
<p><small><i>View <a href='https://demo/testReport/'>Test Report</a></i></small><br></p>
"
- alias: "Notify On Build Passing"
trigger:
- platform: webhook
webhook_id: build_passed
action:
service: notify.cisco_webex_teams_notify
data:
title: "<strong>✅ Version 0.89.7 passed all tests and deployed to production!</strong>"
message: "<blockquote class=info>Version 0.89.7 passed all verifications.
<ul>
<li> test_cov
<li> test_osx
<li> test_win
<li> test_linux
<li>... and 45 more.
</ul>
"
from webexteamssdk import WebexTeamsAPI
api = WebexTeamsAPI()
all_rooms = api.rooms.list(type='group')
for room in all_rooms:
print room.title + " " + room.id
print len(list(all_rooms))
This is listing Group spaces and SOME 1:1 Direct spaces for me!
Not sure if it is on my end, but when I pass a URL the script bombs. Tried both as a string and a string in a list....
test_url="https://upload.wikimedia.org/wikipedia/commons/thumb/6/64/Cisco_logo.svg/375px-Cisco_logo.svg.png"
Traceback (most recent call last):
File "testapi.py", line 37, in <module>
message = api.messages.create(room.id,None,None,None,None,test_url)
File "/usr/local/lib/python2.7/dist-packages/ciscosparkapi/api/messages.py", line 217, in create
assert files is None or isinstance(files, list)
AssertionError
test_url=["https://upload.wikimedia.org/wikipedia/commons/thumb/6/64/Cisco_logo.svg/375px-Cisco_logo.svg.png"]
Traceback (most recent call last):
File "testapi.py", line 37, in <module>
message = api.messages.create(room.id,None,None,None,None,test_url)
File "/usr/local/lib/python2.7/dist-packages/ciscosparkapi/api/messages.py", line 239, in create
post_data[u'files'] = utf8(files)
File "/usr/local/lib/python2.7/dist-packages/ciscosparkapi/helper.py", line 98, in utf8
assert isinstance(string, basestring)
AssertionError
@sQu4rks awesome job with the Guest Issuer PR. Thank you for submitting it!
I have merged it in, and I will push a new release with the added functionality shortly. However, we need to create some test and docs for the new functionality to ensure its reliability and to let other developers know how to use it. Can you take a run at creating the tests and docs? I'm glad to edit and review.
The max parameter does not actually limit the number of items retrieved.
The reason for this seems to be lines 79 through 90 in restsession.py
After calling requests.get() [line 77], the response is checked and yielded [lines 81 and 82]. But then the next page is requested and assigned to the response variable [lines 84 through 87], and the while loop [line 79] reprocesses the variable.
A simple solution would be to remove the loop altogether, leaving just lines 80 through 82; but I don't know if the paged behaviour is needed by other methods.
When I search by displayName or email, if my search is not successful, then the I get an error saying:
'items' object not found in JSON data: {'items': []}
However, I believe the library should handle the error with a response like:
Search returned 0 results
I'm talking about these lines: https://github.com/CiscoDevNet/ciscosparkapi/blob/710aea82b2e227a23372d24c5f98653d32fece63/ciscosparkapi/api/people.py#L141-L144
I'm not sure if you want to handle that kind of events like I'm suggesting. If you want to do it like that, I can help with the code. If not, feel free to close this "issue".
When running the script with the demo script from the readme document I see the following error
File "sparkapitest.py", line 26, in <module> api.message.create(demo_room.id, text="Welcome to the room!", files=["welcome.jpg"]) AttributeError: 'CiscoSparkAPI' object has no attribute 'message'
AttributeError: 'CiscoSparkAPI' object has no attribute 'message'
Used (sudo) PIP to install the library on my Mac.
Is this a known error?
Currently exception messages are extremely big.
I propose suppress exception response sent from Spark server, leaving only necessary error message by default. Response could be still accessed as an exception attribute.
Team,
api.messages.list returns messages in descending order (creation date)
Messages in the Webex Teams client are displayed in ascending order.
Q: How can I sort the messages?
When I run the messages.list() method it returns all messages in a room even with a max argument.
Sample code
from ciscosparkapi import CiscoSparkAPI
api = CiscoSparkAPI()
messages = api.messages.list(roomId="ROOMID", max=10)
Expected: 10 messages returned
Actual: every message in the room is returned.
We need to add a reference to python in the name (to differentiate from other SDKs)
and also change the api suffix (as this is repo is more about being a client or sdk, and not a Web API).
Would be nice if in addition to the generator container, you offered a method of receiving plain JSON. This would allow more flexibility in data manipulation, e.g., loading spark data into pandas, etc
For instance:
sparkDataJSON = api.rooms.list(format="json")
Method to update a webhook fails with error.
webhook = api.webhooks.update(webhookId=whId,
targetUrl=webhook_url,
name=webhook_name)
Cause due to the update method using POST instead of PUT.
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.