Repository: line/line-bot-sdk-python
Pull request #318 (2021-02-19 07:40:35)

GitHub Actions Integration
- Change Travis CI to GitHub Actions
- Fix tox.ini
- Modify README


Patch:
diff --git a/.github/workflows/publish-to-pypi.yml b/.github/workflows/publish-to-pypi.yml
new file mode 100644
index 0000000..1a03a7b
--- /dev/null
+++ b/.github/workflows/publish-to-pypi.yml
@@ -0,0 +1,31 @@
+# This workflow will upload a Python Package using Twine when a release is created
+# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries
+
+name: Upload Python Package
+
+on:
+  release:
+    types: [created]
+
+jobs:
+  deploy:
+
+    runs-on: ubuntu-latest
+
+    steps:
+    - uses: actions/checkout@v2
+    - name: Set up Python
+      uses: actions/setup-python@v2
+      with:
+        python-version: '3.x'
+    - name: Install dependencies
+      run: |
+        python -m pip install --upgrade pip
+        pip install setuptools wheel twine
+    - name: Build and publish
+      env:
+        TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
+        TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
+      run: |
+        python setup.py sdist bdist_wheel
+        twine upload dist/*
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 7e26930..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,15 +0,0 @@
-dist: xenial
-
-language: python
-
-python:
-  - "2.7"
-  - "3.4"
-  - "3.5"
-  - "3.6"
-  - "3.7"
-  - "3.8"
-
-install: pip install tox-travis
-
-script: tox
diff --git a/README.rst b/README.rst
index 6ca5010..cb2aed6 100644
--- a/README.rst
+++ b/README.rst
@@ -1,7 +1,7 @@
 LINE Messaging API SDK for Python
 =================================
 
-|Build Status| |PyPI version| |Documentation Status|
+|Build Status| |Documentation Status|
 
 SDK of the LINE Messaging API for Python.
 
@@ -1276,11 +1276,10 @@ Run tests
 
 Test by using tox. We test against the following versions.
 
--  2.7
--  3.4
--  3.5
 -  3.6
 -  3.7
+-  3.8
+-  3.9
 
 To run all tests and to run ``flake8`` against all versions, use:
 
@@ -1288,19 +1287,18 @@ To run all tests and to run ``flake8`` against all versions, use:
 
     tox
 
-To run all tests against version 2.7, use:
+To run all tests against version 3.6, use:
 
 ::
 
-    $ tox -e py27
+    $ tox -e py3.6
 
-To run a test against version 2.7 and against a specific file, use:
+To run a test against version 3.6 and against a specific file, use:
 
 ::
 
-    $ tox -e py27 -- tests/test_webhook.py
+    $ tox -e py3.6 -- tests/test_webhook.py
 
-And more... TBD
 
 .. |Build Status| image:: https://travis-ci.org/line/line-bot-sdk-python.svg?branch=master
    :target: https://travis-ci.org/line/line-bot-sdk-python
diff --git a/linebot/api.py b/linebot/api.py
index 9565c61..8bc5df0 100644
--- a/linebot/api.py
+++ b/linebot/api.py
@@ -30,7 +30,7 @@ from .models import (
     InsightMessageEventResponse, BroadcastResponse, NarrowcastResponse,
     MessageProgressNarrowcastResponse, BotInfo, GetWebhookResponse, TestWebhookResponse,
 )
-from .models.responses import Group
+from .models.responses import Group, UserIds
 
 
 class LineBotApi(object):
@@ -1218,6 +1218,29 @@ class LineBotApi(object):
 
         return TestWebhookResponse.new_from_json_dict(response.json)
 
+    def get_followers_ids(self, start=None, timeout=None):
+        """Get a list of users who added your LINE Official Account as a friend.
+
+        https://developers.line.biz/en/reference/messaging-api/#get-follower-ids
+
+        :param str start: Get the next array of user IDs.
+        :param timeout: (optional) How long to wait for the server
+            to send data before giving up, as a float,
+            or a (connect timeout, read timeout) float tuple.
+            Default is self.http_client.timeout
+        :type timeout: float | tuple(float, float)
+        :rtype: :py:class:`linebot.models.responses.UserIds`
+        """
+        params = None if start is None else {'start': start}
+
+        response = self._get(
+            '/v2/bot/followers/ids',
+            params=params,
+            timeout=timeout
+        )
+
+        return UserIds.new_from_json_dict(response.json)
+
     def _get(self, path, endpoint=None, params=None, headers=None, stream=False, timeout=None):
         url = (endpoint or self.endpoint) + path
 
diff --git a/linebot/models/__init__.py b/linebot/models/__init__.py
index be0140e..9f7bc0a 100644
--- a/linebot/models/__init__.py
+++ b/linebot/models/__init__.py
@@ -200,3 +200,7 @@ from .background import (  # noqa
     Background,
     LinearGradientBackground,
 )
+
+from .emojis import (  # noqa
+    Emojis
+)
diff --git a/linebot/models/messages.py b/linebot/models/messages.py
index fc51975..9709481 100644
--- a/linebot/models/messages.py
+++ b/linebot/models/messages.py
@@ -54,7 +54,7 @@ class TextMessage(Message):
 
         :param str id: Message ID
         :param str text: Message text
-        :param List sticon: Array of LINE emoji objects
+        :param List emojis: Array of LINE emoji objects
         :param object mention: LINE mention object
         :param kwargs:
         """
diff --git a/linebot/models/responses.py b/linebot/models/responses.py
index f6d0d4c..7593daa 100644
--- a/linebot/models/responses.py
+++ b/linebot/models/responses.py
@@ -572,3 +572,26 @@ class TestWebhookResponse(Base):
         self.status_code = status_code
         self.reason = reason
         self.detail = detail
+
+
+class UserIds(Base):
+    """UserIds.
+
+    https://developers.line.biz/en/reference/messaging-api/#get-follower-ids
+    """
+
+    def __init__(self, user_ids=None, next=None, **kwargs):
+        """__init__ method.
+
+        :param user_ids: List of user IDs of users
+            that have added the LINE Official Account as a friend.
+            Max: 300 user IDs
+        :type user_ids: list[str]
+        :param str next: continuationToken.
+            A continuation token to get the next array of user IDs.
+        :param kwargs:
+        """
+        super(UserIds, self).__init__(**kwargs)
+
+        self.user_ids = user_ids
+        self.next = next
diff --git a/linebot/models/send_messages.py b/linebot/models/send_messages.py
index dc9c534..eed26bf 100644
--- a/linebot/models/send_messages.py
+++ b/linebot/models/send_messages.py
@@ -20,6 +20,7 @@ from abc import ABCMeta
 
 from future.utils import with_metaclass
 
+from .emojis import Emojis
 from .actions import get_action
 from .base import Base
 
@@ -49,7 +50,7 @@ class TextSendMessage(SendMessage):
     https://developers.line.biz/en/reference/messaging-api/#text-message
     """
 
-    def __init__(self, text=None, quick_reply=None, **kwargs):
+    def __init__(self, text=None, emojis=None, quick_reply=None, **kwargs):
         """__init__ method.
 
         :param str text: Message text
@@ -61,6 +62,17 @@ class TextSendMessage(SendMessage):
 
         self.type = 'text'
         self.text = text
+        if emojis:
+            new_emojis = []
+            for emoji in emojis:
+                emoji_object = self.get_or_new_from_json_dict(
+                    emoji, Emojis
+                )
+                if emoji_object:
+                    new_emojis.append(emoji_object)
+            self.emojis = new_emojis
+        else:
+            self.emojis = emojis
 
 
 class ImageSendMessage(SendMessage):
diff --git a/tox.ini b/tox.ini
index 35a3cc0..3ee8862 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
 [tox]
-envlist = py27, py34, py35, py36, py37, py38, py37-flake8-src, py37-flake8-other
+envlist = py3.6, py3.7, py3.8, py3.9, py3-flake8-src, py3-flake8-other
 
 [testenv]
 deps =
@@ -8,21 +8,21 @@ deps =
     -r{toxinidir}/requirements-test.txt
 commands = py.test -v tests/
 
-[testenv:py37]
-basepython = python3.7
+[testenv:py3]
+basepython = python3
 deps = {[testenv]deps}
 commands = py.test -v --cov=linebot tests/
 
-[testenv:py37-flake8-src]
-basepython = python3.7
+[testenv:py3-flake8-src]
+basepython = python3
 deps =
     six
     flake8
     flake8-docstrings
 commands = flake8 linebot/
 
-[testenv:py37-flake8-other]
-basepython = python3.7
+[testenv:py3-flake8-other]
+basepython = python3
 deps =
     six
     flake8


Test patch:
diff --git a/.github/workflows/auto-testing.yml b/.github/workflows/auto-testing.yml
new file mode 100644
index 0000000..5653e0b
--- /dev/null
+++ b/.github/workflows/auto-testing.yml
@@ -0,0 +1,27 @@
+# This workflow will install Python dependencies, run tests and lint with a variety of Python versions
+# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
+
+name: Python package
+
+on: [push, pull_request]
+jobs:
+  build:
+
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        python-version: [3.6, 3.7, 3.8, 3.9]
+
+    steps:
+    - uses: actions/checkout@v2
+    - name: Set up Python ${{ matrix.python-version }}
+      uses: actions/setup-python@v2
+      with:
+        python-version: ${{ matrix.python-version }}
+    - name: Install dependencies
+      run: |
+        python -m pip install --upgrade pip
+        if [ -f requirements-dev.txt ]; then python -m pip install -r requirements-dev.txt; fi
+    - name: Test with pytest
+      run: |
+        tox
diff --git a/tests/api/test_get_member_ids.py b/tests/api/test_get_member_ids.py
index 938cd99..e0d2654 100644
--- a/tests/api/test_get_member_ids.py
+++ b/tests/api/test_get_member_ids.py
@@ -117,6 +117,50 @@ class TestLineBotApi(unittest.TestCase):
         self.assertEqual(member_ids_response.member_ids, ['U1', 'U2'])
         self.assertEqual(member_ids_response.next, 'continuationToken2')
 
+    @responses.activate
+    def test_get_follower_user_ids(self):
+        responses.add(
+            responses.GET,
+            LineBotApi.DEFAULT_API_ENDPOINT + '/v2/bot/followers/ids',
+            json={
+                'userIds': ['U1', 'U2']
+            },
+            status=200
+        )
+
+        member_ids_response = self.tested.get_followers_ids()
+
+        request = responses.calls[0].request
+        self.assertEqual(request.method, 'GET')
+        self.assertEqual(
+            request.url,
+            LineBotApi.DEFAULT_API_ENDPOINT + '/v2/bot/followers/ids')
+        self.assertEqual(member_ids_response.user_ids, ['U1', 'U2'])
+        self.assertEqual(member_ids_response.next, None)
+
+    @responses.activate
+    def test_get_follower_user_ids_with_start(self):
+        responses.add(
+            responses.GET,
+            LineBotApi.DEFAULT_API_ENDPOINT + '/v2/bot/followers/ids',
+            json={
+                'userIds': ['U1', 'U2'],
+                'next': 'continuationToken2'
+            },
+            status=200
+        )
+
+        member_ids_response = self.tested.get_followers_ids(start='continuationToken1')
+
+        request = responses.calls[0].request
+        self.assertEqual(request.method, 'GET')
+        self.assertEqual(
+            request.url,
+            LineBotApi.DEFAULT_API_ENDPOINT +
+            '/v2/bot/followers/ids?start=continuationToken1')
+        self.assertEqual(member_ids_response.user_ids, ['U1', 'U2'])
+        self.assertEqual(member_ids_response.next, 'continuationToken2')
+
 
 if __name__ == '__main__':
     unittest.main()