From 2701f69d17c3537b89edc73310e46bf16798457f Mon Sep 17 00:00:00 2001 From: Fabio Manganiello Date: Sat, 30 Dec 2023 16:28:12 +0100 Subject: [PATCH 1/4] [#287] Migrated `github` from a backend to a runnable plugin. Closes: #287 --- docs/source/backends.rst | 1 - docs/source/platypush/backend/github.rst | 5 -- docs/source/platypush/plugins/github.rst | 5 ++ docs/source/plugins.rst | 1 + platypush/backend/github/manifest.yaml | 32 --------- .../{backend => plugins}/github/__init__.py | 70 ++++++++----------- platypush/plugins/github/manifest.yaml | 20 ++++++ 7 files changed, 57 insertions(+), 77 deletions(-) delete mode 100644 docs/source/platypush/backend/github.rst create mode 100644 docs/source/platypush/plugins/github.rst delete mode 100644 platypush/backend/github/manifest.yaml rename platypush/{backend => plugins}/github/__init__.py (81%) create mode 100644 platypush/plugins/github/manifest.yaml diff --git a/docs/source/backends.rst b/docs/source/backends.rst index 9e8464cf..1347f2e3 100644 --- a/docs/source/backends.rst +++ b/docs/source/backends.rst @@ -11,7 +11,6 @@ Backends platypush/backend/camera.pi.rst platypush/backend/chat.telegram.rst platypush/backend/foursquare.rst - platypush/backend/github.rst platypush/backend/google.fit.rst platypush/backend/google.pubsub.rst platypush/backend/gps.rst diff --git a/docs/source/platypush/backend/github.rst b/docs/source/platypush/backend/github.rst deleted file mode 100644 index a299331d..00000000 --- a/docs/source/platypush/backend/github.rst +++ /dev/null @@ -1,5 +0,0 @@ -``github`` -============================ - -.. automodule:: platypush.backend.github - :members: diff --git a/docs/source/platypush/plugins/github.rst b/docs/source/platypush/plugins/github.rst new file mode 100644 index 00000000..483d5040 --- /dev/null +++ b/docs/source/platypush/plugins/github.rst @@ -0,0 +1,5 @@ +``github`` +========== + +.. automodule:: platypush.plugins.github + :members: diff --git a/docs/source/plugins.rst b/docs/source/plugins.rst index 8f39bf6e..070ff267 100644 --- a/docs/source/plugins.rst +++ b/docs/source/plugins.rst @@ -35,6 +35,7 @@ Plugins platypush/plugins/file.rst platypush/plugins/file.monitor.rst platypush/plugins/foursquare.rst + platypush/plugins/github.rst platypush/plugins/google.calendar.rst platypush/plugins/google.drive.rst platypush/plugins/google.fit.rst diff --git a/platypush/backend/github/manifest.yaml b/platypush/backend/github/manifest.yaml deleted file mode 100644 index 18eced00..00000000 --- a/platypush/backend/github/manifest.yaml +++ /dev/null @@ -1,32 +0,0 @@ -manifest: - events: - platypush.message.event.github.GithubCommitCommentEvent: when a new commit comment - is created. - platypush.message.event.github.GithubCreateEvent: when a tag or branch is created. - platypush.message.event.github.GithubDeleteEvent: when a tag or branch is deleted. - platypush.message.event.github.GithubEvent: for any event that doesn't fall in - the above categories(``event_type`` will be set accordingly). - platypush.message.event.github.GithubForkEvent: when a user forks a repository. - platypush.message.event.github.GithubIssueCommentEvent: when new activity happens - on an issue comment. - platypush.message.event.github.GithubIssueEvent: when new repository issue activity - happens. - platypush.message.event.github.GithubMemberEvent: when new repository collaborators - activity happens. - platypush.message.event.github.GithubPublicEvent: when a repository goes public. - platypush.message.event.github.GithubPullRequestEvent: when new pull request related - activity happens. - platypush.message.event.github.GithubPullRequestReviewCommentEvent: when activity - happens on a pullrequest commit. - platypush.message.event.github.GithubPushEvent: when a new push is created. - platypush.message.event.github.GithubReleaseEvent: when a new release happens. - platypush.message.event.github.GithubSponsorshipEvent: when new sponsorship related - activity happens. - platypush.message.event.github.GithubWatchEvent: when someone stars/starts watching - a repository. - platypush.message.event.github.GithubWikiEvent: when new activity happens on a - repository wiki. - install: - pip: [] - package: platypush.backend.github - type: backend diff --git a/platypush/backend/github/__init__.py b/platypush/plugins/github/__init__.py similarity index 81% rename from platypush/backend/github/__init__.py rename to platypush/plugins/github/__init__.py index a66b6364..1e3755e4 100644 --- a/platypush/backend/github/__init__.py +++ b/platypush/plugins/github/__init__.py @@ -8,7 +8,6 @@ import requests from sqlalchemy import create_engine, Column, String, DateTime from sqlalchemy.orm import sessionmaker, scoped_session -from platypush.backend import Backend from platypush.common.db import declarative_base from platypush.config import Config from platypush.message.event.github import ( @@ -29,11 +28,13 @@ from platypush.message.event.github import ( GithubSponsorshipEvent, GithubWatchEvent, ) +from platypush.plugins import RunnablePlugin Base = declarative_base() Session = scoped_session(sessionmaker()) +# pylint: disable=too-few-public-methods class GithubResource(Base): """ Models the GithubLastEvent table, containing the timestamp where a certain URL was last checked. @@ -44,9 +45,9 @@ class GithubResource(Base): last_updated_at = Column(DateTime) -class GithubBackend(Backend): +class GithubPlugin(RunnablePlugin): """ - This backend monitors for notifications and events either on Github user, organization or repository level. + This plugin monitors for notifications and events either on Github user, organization or repository level. You'll need a Github personal access token to use the service. To get one: - Access your Github profile settings @@ -54,7 +55,7 @@ class GithubBackend(Backend): - Select *Personal access tokens* - Click *Generate new token* - This backend requires the following permissions: + This plugin requires the following permissions: - ``repo`` - ``notifications`` @@ -72,22 +73,21 @@ class GithubBackend(Backend): org: Optional[str] = None, poll_seconds: int = 60, max_events_per_scan: Optional[int] = 10, - *args, - **kwargs + **kwargs, ): """ - If neither ``repos`` nor ``org`` is specified then the backend will monitor all new events on user level. + If neither ``repos`` nor ``org`` is specified then the plugin will monitor all new events on user level. :param user: Github username. :param user_token: Github personal access token. :param repos: List of repos to be monitored - if a list is provided then only these repositories will be monitored for events. Repositories should be passed in the format ``username/repository``. :param org: Organization to be monitored - if provided then only this organization will be monitored for events. - :param poll_seconds: How often the backend should check for new events, in seconds (default: 60). + :param poll_seconds: How often the plugin should check for new events, in seconds (default: 60). :param max_events_per_scan: Maximum number of events per resource that will be triggered if there is a large number of events/notification since the last check (default: 10). Specify 0 or null for no limit. """ - super().__init__(*args, **kwargs) + super().__init__(**kwargs) self._last_text: Optional[str] = None self.user = user self.user_token = user_token @@ -95,7 +95,7 @@ class GithubBackend(Backend): self.org = org self.poll_seconds = poll_seconds self.db_lock = threading.RLock() - self.workdir = os.path.join(os.path.expanduser(Config.get('workdir')), 'github') + self.workdir = os.path.join(os.path.expanduser(Config.get_workdir()), 'github') self.dbfile = os.path.join(self.workdir, 'github.db') self.max_events_per_scan = max_events_per_scan @@ -103,8 +103,8 @@ class GithubBackend(Backend): self._init_db() def _request(self, uri: str, method: str = 'get') -> dict: - method = getattr(requests, method.lower()) - return method( + func = getattr(requests, method.lower()) + return func( self._base_url + uri, auth=(self.user, self.user_token), headers={'Accept': 'application/vnd.github.v3+json'}, @@ -112,7 +112,7 @@ class GithubBackend(Backend): def _init_db(self): engine = create_engine( - 'sqlite:///{}'.format(self.dbfile), + f'sqlite:///{self.dbfile}', connect_args={'check_same_thread': False}, ) Base.metadata.create_all(engine) @@ -124,7 +124,7 @@ class GithubBackend(Backend): return datetime.datetime.fromisoformat(time_string[:-1] + '+00:00') @staticmethod - def _get_or_create_resource(uri: str, session: Session) -> GithubResource: + def _get_or_create_resource(uri: str, session: scoped_session) -> GithubResource: record = session.query(GithubResource).filter_by(uri=uri).first() if record is None: record = GithubResource(uri=uri) @@ -135,18 +135,18 @@ class GithubBackend(Backend): def _get_last_event_time(self, uri: str): with self.db_lock: - record = self._get_or_create_resource(uri=uri, session=Session()) + record = self._get_or_create_resource(uri=uri, session=Session()) # type: ignore return ( record.last_updated_at.replace(tzinfo=datetime.timezone.utc) - if record.last_updated_at + if record.last_updated_at # type: ignore else None ) def _update_last_event_time(self, uri: str, last_updated_at: datetime.datetime): with self.db_lock: session = Session() - record = self._get_or_create_resource(uri=uri, session=session) - record.last_updated_at = last_updated_at + record = self._get_or_create_resource(uri=uri, session=session) # type: ignore + record.last_updated_at = last_updated_at # type: ignore session.add(record) session.commit() @@ -211,16 +211,17 @@ class GithubBackend(Backend): fired_events.append(self._parse_event(event)) for event in fired_events: - self.bus.post(event) + self._bus.post(event) - self._update_last_event_time( - uri=uri, last_updated_at=new_last_event_time - ) + if new_last_event_time: + self._update_last_event_time( + uri=uri, last_updated_at=new_last_event_time + ) except Exception as e: self.logger.warning( - 'Encountered exception while fetching events from {}: {}'.format( - uri, str(e) - ) + 'Encountered exception while fetching events from %s: %s', + uri, + e, ) self.logger.exception(e) @@ -229,45 +230,36 @@ class GithubBackend(Backend): return thread - def run(self): - self.logger.info('Starting Github backend') + def main(self): monitors = [] if self.repos: for repo in self.repos: monitors.append( threading.Thread( - target=self._events_monitor( - '/networks/{repo}/events'.format(repo=repo) - ) + target=self._events_monitor(f'/networks/{repo}/events') ) ) + if self.org: monitors.append( threading.Thread( - target=self._events_monitor( - '/orgs/{org}/events'.format(org=self.org) - ) + target=self._events_monitor(f'/orgs/{self.org}/events') ) ) if not (self.repos or self.org): monitors.append( threading.Thread( - target=self._events_monitor( - '/users/{user}/events'.format(user=self.user) - ) + target=self._events_monitor(f'/users/{self.user}/events') ) ) for monitor in monitors: monitor.start() - self.logger.info('Started Github backend') for monitor in monitors: monitor.join() - self.logger.info('Github backend terminated') - # vim:sw=4:ts=4:et: diff --git a/platypush/plugins/github/manifest.yaml b/platypush/plugins/github/manifest.yaml new file mode 100644 index 00000000..7d6ff3e4 --- /dev/null +++ b/platypush/plugins/github/manifest.yaml @@ -0,0 +1,20 @@ +manifest: + events: + - platypush.message.event.github.GithubCommitCommentEvent + - platypush.message.event.github.GithubCreateEvent + - platypush.message.event.github.GithubDeleteEvent + - platypush.message.event.github.GithubEvent + - platypush.message.event.github.GithubForkEvent + - platypush.message.event.github.GithubIssueCommentEvent + - platypush.message.event.github.GithubIssueEvent + - platypush.message.event.github.GithubMemberEvent + - platypush.message.event.github.GithubPublicEvent + - platypush.message.event.github.GithubPullRequestEvent + - platypush.message.event.github.GithubPullRequestReviewCommentEvent + - platypush.message.event.github.GithubPushEvent + - platypush.message.event.github.GithubReleaseEvent + - platypush.message.event.github.GithubSponsorshipEvent + - platypush.message.event.github.GithubWatchEvent + - platypush.message.event.github.GithubWikiEvent + package: platypush.plugin.github + type: plugin From 7f13e02803f0652b1ce26037eb7fe94db170033e Mon Sep 17 00:00:00 2001 From: Fabio Manganiello Date: Sun, 31 Dec 2023 04:34:42 +0100 Subject: [PATCH 2/4] Fixed docstring --- platypush/plugins/github/__init__.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/platypush/plugins/github/__init__.py b/platypush/plugins/github/__init__.py index 1e3755e4..825eed5c 100644 --- a/platypush/plugins/github/__init__.py +++ b/platypush/plugins/github/__init__.py @@ -61,6 +61,7 @@ class GithubPlugin(RunnablePlugin): - ``notifications`` - ``read:org`` if you want to access repositories on organization level. + If neither ``repos`` nor ``org`` is specified then the plugin will monitor all new events on user level. """ _base_url = 'https://api.github.com' @@ -76,8 +77,6 @@ class GithubPlugin(RunnablePlugin): **kwargs, ): """ - If neither ``repos`` nor ``org`` is specified then the plugin will monitor all new events on user level. - :param user: Github username. :param user_token: Github personal access token. :param repos: List of repos to be monitored - if a list is provided then only these repositories will be From efe46386f4137b920cd1b671191f1e4b32edc2e8 Mon Sep 17 00:00:00 2001 From: Fabio Manganiello Date: Mon, 1 Jan 2024 14:27:18 +0100 Subject: [PATCH 3/4] [Entities UI] Reverted `.extension-container` max-width. --- .../http/webapp/src/components/panels/Extensions/Index.vue | 2 -- 1 file changed, 2 deletions(-) diff --git a/platypush/backend/http/webapp/src/components/panels/Extensions/Index.vue b/platypush/backend/http/webapp/src/components/panels/Extensions/Index.vue index ce05307f..77447c03 100644 --- a/platypush/backend/http/webapp/src/components/panels/Extensions/Index.vue +++ b/platypush/backend/http/webapp/src/components/panels/Extensions/Index.vue @@ -250,8 +250,6 @@ $header-height: 3.25em; } .extension-container { - max-width: calc(100% - 1em); - .extension { display: flex; flex-direction: column; From 4aeff10a5d1ef417746a4689dc67c4a1f4960c24 Mon Sep 17 00:00:00 2001 From: Fabio Manganiello Date: Mon, 1 Jan 2024 22:52:18 +0100 Subject: [PATCH 4/4] [#286] Merge Foursquare backend and plugin. Closes: #286 --- docs/source/backends.rst | 1 - docs/source/platypush/backend/foursquare.rst | 5 - platypush/backend/foursquare/__init__.py | 54 --------- platypush/backend/foursquare/manifest.yaml | 8 -- platypush/plugins/foursquare/__init__.py | 119 +++++++++++++------ platypush/plugins/foursquare/manifest.yaml | 3 +- 6 files changed, 85 insertions(+), 105 deletions(-) delete mode 100644 docs/source/platypush/backend/foursquare.rst delete mode 100644 platypush/backend/foursquare/__init__.py delete mode 100644 platypush/backend/foursquare/manifest.yaml diff --git a/docs/source/backends.rst b/docs/source/backends.rst index 1347f2e3..4b9c06aa 100644 --- a/docs/source/backends.rst +++ b/docs/source/backends.rst @@ -10,7 +10,6 @@ Backends platypush/backend/button.flic.rst platypush/backend/camera.pi.rst platypush/backend/chat.telegram.rst - platypush/backend/foursquare.rst platypush/backend/google.fit.rst platypush/backend/google.pubsub.rst platypush/backend/gps.rst diff --git a/docs/source/platypush/backend/foursquare.rst b/docs/source/platypush/backend/foursquare.rst deleted file mode 100644 index 34f0af8e..00000000 --- a/docs/source/platypush/backend/foursquare.rst +++ /dev/null @@ -1,5 +0,0 @@ -``foursquare`` -================================ - -.. automodule:: platypush.backend.foursquare - :members: diff --git a/platypush/backend/foursquare/__init__.py b/platypush/backend/foursquare/__init__.py deleted file mode 100644 index 29f2122a..00000000 --- a/platypush/backend/foursquare/__init__.py +++ /dev/null @@ -1,54 +0,0 @@ -from typing import Optional - -from platypush.backend import Backend -from platypush.context import get_plugin -from platypush.message.event.foursquare import FoursquareCheckinEvent - - -class FoursquareBackend(Backend): - """ - This backend polls for new check-ins on the user's Foursquare account and triggers an event when a new check-in - occurs. - - Requires: - - * The :class:`platypush.plugins.foursquare.FoursquarePlugin` plugin configured and enabled. - - """ - - _last_created_at_varname = '_foursquare_checkin_last_created_at' - - def __init__(self, poll_seconds: Optional[float] = 60.0, *args, **kwargs): - """ - :param poll_seconds: How often the backend should check for new check-ins (default: one minute). - """ - super().__init__(*args, poll_seconds=poll_seconds, **kwargs) - self._last_created_at = None - - def __enter__(self): - self._last_created_at = int( - get_plugin('variable') - .get(self._last_created_at_varname) - .output.get(self._last_created_at_varname) - or 0 - ) - self.logger.info('Started Foursquare backend') - - def loop(self): - checkins = get_plugin('foursquare').get_checkins().output - if not checkins: - return - - last_checkin = checkins[0] - last_checkin_created_at = last_checkin.get('createdAt', 0) - if self._last_created_at and last_checkin_created_at <= self._last_created_at: - return - - self.bus.post(FoursquareCheckinEvent(checkin=last_checkin)) - self._last_created_at = last_checkin_created_at - get_plugin('variable').set( - **{self._last_created_at_varname: self._last_created_at} - ) - - -# vim:sw=4:ts=4:et: diff --git a/platypush/backend/foursquare/manifest.yaml b/platypush/backend/foursquare/manifest.yaml deleted file mode 100644 index 0690cc93..00000000 --- a/platypush/backend/foursquare/manifest.yaml +++ /dev/null @@ -1,8 +0,0 @@ -manifest: - events: - platypush.message.event.foursquare.FoursquareCheckinEvent: when a new check-in - occurs. - install: - pip: [] - package: platypush.backend.foursquare - type: backend diff --git a/platypush/plugins/foursquare/__init__.py b/platypush/plugins/foursquare/__init__.py index 79d4b572..06ba0b1b 100644 --- a/platypush/plugins/foursquare/__init__.py +++ b/platypush/plugins/foursquare/__init__.py @@ -1,14 +1,19 @@ import datetime -import requests from typing import List, Dict, Any, Optional, Union, Tuple -from platypush.plugins import Plugin, action +import requests + +from platypush.context import Variable +from platypush.message.event.foursquare import FoursquareCheckinEvent +from platypush.plugins import RunnablePlugin, action -class FoursquarePlugin(Plugin): +class FoursquarePlugin(RunnablePlugin): """ Plugin to interact with the `Foursquare Places API `_. + It also raises events when a new check-in occurs on the user's account. + In order to enable the Foursquare API on your account you need to: - Create a new app on the `Foursquare developers website `_. @@ -24,20 +29,36 @@ class FoursquarePlugin(Plugin): """ api_base_url = 'https://api.foursquare.com/v2' + _last_created_at_varname = '_foursquare_checkin_last_created_at' + _http_timeout = 10 - def __init__(self, access_token: str, **kwargs): + def __init__(self, access_token: str, poll_interval: float = 120, **kwargs): """ :param access_token: The access token to use to authenticate to the Foursquare API. """ - super().__init__(**kwargs) + super().__init__(poll_interval=poll_interval, **kwargs) self.access_token = access_token + self._last_created_at = Variable(self._last_created_at_varname) def _get_url(self, endpoint): - return '{url}/{endpoint}?oauth_token={token}&v={version}'.format( - url=self.api_base_url, - endpoint=endpoint, - token=self.access_token, - version=datetime.date.today().strftime('%Y%m%d'), + return ( + self.api_base_url + + '/' + + endpoint + + '?oauth_token=' + + self.access_token + + '&v=' + + datetime.date.today().strftime('%Y%m%d') + ) + + def _get_checkins(self) -> List[Dict[str, Any]]: + url = self._get_url('users/self/checkins') + return ( + requests.get(url, timeout=self._http_timeout) + .json() + .get('response', {}) + .get('checkins', {}) + .get('items', []) ) @action @@ -46,16 +67,8 @@ class FoursquarePlugin(Plugin): Get the list of check-ins of the current user. :return: A list of checkins, as returned by the Foursquare API. """ - url = self._get_url('users/self/checkins') - return ( - requests.get(url) - .json() - .get('response', {}) - .get('checkins', {}) - .get('items', []) - ) + return self._get_checkins() - # noinspection DuplicatedCode @action def search( self, @@ -67,7 +80,7 @@ class FoursquarePlugin(Plugin): near: Optional[str] = None, query: Optional[str] = None, limit: Optional[int] = None, - url: Optional[int] = None, + url: Optional[str] = None, categories: Optional[List[str]] = None, radius: Optional[int] = None, sw: Optional[Union[Tuple[float], List[float]]] = None, @@ -123,12 +136,15 @@ class FoursquarePlugin(Plugin): if ne: args['ne'] = ne - url = self._get_url('venues/search') return ( - requests.get(url, params=args).json().get('response', {}).get('venues', []) + requests.get( + self._get_url('venues/search'), params=args, timeout=self._http_timeout + ) + .json() + .get('response', {}) + .get('venues', []) ) - # noinspection DuplicatedCode @action def explore( self, @@ -223,7 +239,10 @@ class FoursquarePlugin(Plugin): url = self._get_url('venues/explore') return ( - requests.get(url, params=args).json().get('response', {}).get('venues', []) + requests.get(url, params=args, timeout=self._http_timeout) + .json() + .get('response', {}) + .get('venues', []) ) @action @@ -263,7 +282,10 @@ class FoursquarePlugin(Plugin): url = self._get_url('venues/trending') return ( - requests.get(url, params=args).json().get('response', {}).get('venues', []) + requests.get(url, params=args, timeout=self._http_timeout) + .json() + .get('response', {}) + .get('venues', []) ) @staticmethod @@ -275,7 +297,7 @@ class FoursquarePlugin(Plugin): assert isinstance( t, datetime.datetime - ), 'Cannot parse object of type {} into datetime: {}'.format(type(t), t) + ), f'Cannot parse object of type {type(t)} into datetime: {t}' return t @action @@ -306,7 +328,10 @@ class FoursquarePlugin(Plugin): url = self._get_url('venues/timeseries') return ( - requests.get(url, params=args).json().get('response', {}).get('venues', []) + requests.get(url, params=args, timeout=self._http_timeout) + .json() + .get('response', {}) + .get('venues', []) ) @action @@ -326,13 +351,16 @@ class FoursquarePlugin(Plugin): :return: A list of venues, as returned by the Foursquare API. """ args = { - 'startAt': self._parse_time(start_at), - 'endAt': self._parse_time(end_at), + 'startAt': self._parse_time(start_at).isoformat(), + 'endAt': self._parse_time(end_at).isoformat(), } - url = self._get_url('venues/{}/stats'.format(venue_id)) + url = self._get_url(f'venues/{venue_id}/stats') return ( - requests.get(url, params=args).json().get('response', {}).get('venues', []) + requests.get(url, params=args, timeout=self._http_timeout) + .json() + .get('response', {}) + .get('venues', []) ) @action @@ -343,7 +371,7 @@ class FoursquarePlugin(Plugin): """ url = self._get_url('venues/managed') return ( - requests.get(url) + requests.get(url, timeout=self._http_timeout) .json() .get('response', {}) .get('venues', []) @@ -386,11 +414,11 @@ class FoursquarePlugin(Plugin): if latitude and longitude: args['ll'] = ','.join([str(latitude), str(longitude)]) if altitude: - args['alt'] = altitude + args['alt'] = str(altitude) if latlng_accuracy: - args['llAcc'] = latlng_accuracy + args['llAcc'] = str(latlng_accuracy) if altitude_accuracy: - args['altAcc'] = altitude_accuracy + args['altAcc'] = str(altitude_accuracy) if shout: args['shout'] = shout if broadcast: @@ -400,8 +428,27 @@ class FoursquarePlugin(Plugin): url = self._get_url('checkins/add') return ( - requests.post(url, data=args).json().get('response', {}).get('checkin', {}) + requests.post(url, data=args, timeout=self._http_timeout) + .json() + .get('response', {}) + .get('checkin', {}) ) + def main(self): + while not self.should_stop(): + checkins = self._get_checkins() + if not checkins: + return + + last_checkin = checkins[0] + last_checkin_created_at = last_checkin.get('createdAt', 0) + last_created_at = self._last_created_at.get() or 0 + if last_created_at and last_checkin_created_at <= last_created_at: + return + + self._bus.post(FoursquareCheckinEvent(checkin=last_checkin)) + self._last_created_at.set(last_checkin_created_at) + self.wait_stop(self.poll_interval) + # vim:sw=4:ts=4:et: diff --git a/platypush/plugins/foursquare/manifest.yaml b/platypush/plugins/foursquare/manifest.yaml index 35acebd0..f9b8cd0f 100644 --- a/platypush/plugins/foursquare/manifest.yaml +++ b/platypush/plugins/foursquare/manifest.yaml @@ -1,5 +1,6 @@ manifest: - events: {} + events: + - platypush.message.event.foursquare.FoursquareCheckinEvent install: pip: [] package: platypush.plugins.foursquare