instagram-message-api-local 0.0.3b4578__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (23) hide show
  1. instagram_message_api_local-0.0.3b4578/PKG-INFO +26 -0
  2. instagram_message_api_local-0.0.3b4578/README.md +43 -0
  3. instagram_message_api_local-0.0.3b4578/instagram_message_api_local/__init__.py +0 -0
  4. instagram_message_api_local-0.0.3b4578/instagram_message_api_local/src/__init__.py +3 -0
  5. instagram_message_api_local-0.0.3b4578/instagram_message_api_local/src/constants_src_the_example_local.py +13 -0
  6. instagram_message_api_local-0.0.3b4578/instagram_message_api_local/src/instagram_message_api_cli.py +20 -0
  7. instagram_message_api_local-0.0.3b4578/instagram_message_api_local/src/instagram_message_api_client.py +39 -0
  8. instagram_message_api_local-0.0.3b4578/instagram_message_api_local/src/instagram_message_api_enum.py +6 -0
  9. instagram_message_api_local-0.0.3b4578/instagram_message_api_local/src/instagram_message_api_local.py +27 -0
  10. instagram_message_api_local-0.0.3b4578/instagram_message_api_local/src/version.py +1 -0
  11. instagram_message_api_local-0.0.3b4578/instagram_message_api_local/tests/__init__.py +0 -0
  12. instagram_message_api_local-0.0.3b4578/instagram_message_api_local/tests/conftest.py +5 -0
  13. instagram_message_api_local-0.0.3b4578/instagram_message_api_local/tests/unit/__init__.py +0 -0
  14. instagram_message_api_local-0.0.3b4578/instagram_message_api_local/tests/unit/instagram_message_api_client_unit_test.py +53 -0
  15. instagram_message_api_local-0.0.3b4578/instagram_message_api_local.egg-info/PKG-INFO +26 -0
  16. instagram_message_api_local-0.0.3b4578/instagram_message_api_local.egg-info/SOURCES.txt +21 -0
  17. instagram_message_api_local-0.0.3b4578/instagram_message_api_local.egg-info/dependency_links.txt +1 -0
  18. instagram_message_api_local-0.0.3b4578/instagram_message_api_local.egg-info/entry_points.txt +2 -0
  19. instagram_message_api_local-0.0.3b4578/instagram_message_api_local.egg-info/requires.txt +2 -0
  20. instagram_message_api_local-0.0.3b4578/instagram_message_api_local.egg-info/top_level.txt +1 -0
  21. instagram_message_api_local-0.0.3b4578/pyproject.toml +30 -0
  22. instagram_message_api_local-0.0.3b4578/setup.cfg +4 -0
  23. instagram_message_api_local-0.0.3b4578/setup.py +80 -0
@@ -0,0 +1,26 @@
1
+ Metadata-Version: 2.4
2
+ Name: instagram-message-api-local
3
+ Version: 0.0.3b4578
4
+ Summary: PyPI instagram-message-api-local Python Package owned by Circlez.ai
5
+ Home-page: https://github.com/circles-zone/instagram-message-api-local-python-package
6
+ Author: Circles
7
+ Author-email: info@circlez.ai
8
+ License: MIT
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Operating System :: OS Independent
11
+ Description-Content-Type: text/markdown
12
+ Requires-Dist: python-sdk-remote
13
+ Requires-Dist: requests
14
+ Dynamic: author
15
+ Dynamic: author-email
16
+ Dynamic: classifier
17
+ Dynamic: description
18
+ Dynamic: description-content-type
19
+ Dynamic: home-page
20
+ Dynamic: license
21
+ Dynamic: requires-dist
22
+ Dynamic: summary
23
+
24
+ PyPI instagram-message-api-local Python Package owned by Circlez.ai
25
+ JIRA Work Item: https://circles-zone.atlassian.net/browse/BU-2614
26
+ GHA: https://github.com/circles-zone/instagram-message-api-local-python-package/actions
@@ -0,0 +1,43 @@
1
+ # General
2
+
3
+ Unlike the README.md in the root directory, this is a README.md of this specific repo/package<br>
4
+
5
+ TODO Please update this README.md of this specific repo/package and not the README.md in the root directory based on the python-package-template repo<br>
6
+
7
+ ## TODOs
8
+
9
+ TODO example of a TODO, please it with a real TODO<br>
10
+
11
+ ## Direct messages
12
+
13
+ Python callers can use `InstagramMessageApiClient`:
14
+
15
+ ```python
16
+ from instagram_message_api_local.src.instagram_message_api_client import InstagramMessageApiClient
17
+
18
+ message_id = InstagramMessageApiClient().send_direct_message(
19
+ recipient_id="17841400000000000", text="Hello from Python")
20
+ ```
21
+
22
+ The equivalent CLI is:
23
+
24
+ ```text
25
+ instagram-message 17841400000000000 "Hello from the CLI"
26
+ ```
27
+
28
+ Live requests require `INSTAGRAM_GRAPH_API_ACCESS_TOKEN` and
29
+ `IS_REALLY_SEND_INSTAGRAM_MESSAGE=true`; otherwise the client performs a dry run.
30
+
31
+ ## Versions
32
+
33
+ [pub] 0.0.1 Initial version (change the directories, files, setup.py, .github/workflows/*.yml)<br>
34
+
35
+ ## Installing instructions
36
+
37
+ It's advised to use venv<br>
38
+ If you have Windows, it's also advised to use Windows Subsystem for Linux<br>
39
+
40
+ ## Install dependencies
41
+
42
+ Run ./download-beta-sdk.sh from the bash terminal<br>
43
+ Run pip install -r requirements.txt from the bash terminal<br>
@@ -0,0 +1,3 @@
1
+ from .instagram_message_api_client import InstagramMessageApiClient
2
+
3
+ __all__ = ["InstagramMessageApiClient"]
@@ -0,0 +1,13 @@
1
+ """Compatibility constants for the original example module."""
2
+
3
+
4
+ class ConstantsSrcTheExampleLocal:
5
+ COMPONENT_NAME = "Instagram Message API Local"
6
+ UNKNOWN_ID = 0
7
+
8
+
9
+ def get_logger_object(category="Code"):
10
+ return {
11
+ "component_name": ConstantsSrcTheExampleLocal.COMPONENT_NAME,
12
+ "component_category": category,
13
+ }
@@ -0,0 +1,20 @@
1
+ """CLI for Instagram direct messages."""
2
+
3
+ import argparse
4
+
5
+ from .instagram_message_api_client import InstagramMessageApiClient
6
+
7
+
8
+ def main(argv=None) -> int:
9
+ parser = argparse.ArgumentParser(prog="instagram-message", description="Send an Instagram direct message")
10
+ parser.add_argument("recipient_id", help="Instagram-scoped user id")
11
+ parser.add_argument("text")
12
+ args = parser.parse_args(argv)
13
+ message_id = InstagramMessageApiClient().send_direct_message(
14
+ recipient_id=args.recipient_id, text=args.text)
15
+ print(message_id or "Dry run: set IS_REALLY_SEND_INSTAGRAM_MESSAGE=true for a live request")
16
+ return 0
17
+
18
+
19
+ if __name__ == "__main__": # pragma: no cover
20
+ raise SystemExit(main())
@@ -0,0 +1,39 @@
1
+ from __future__ import annotations
2
+
3
+ """Small Python client for Instagram's Graph API messaging endpoint."""
4
+
5
+ import os
6
+ from http import HTTPStatus
7
+
8
+ import requests
9
+
10
+
11
+ class InstagramMessageApiClient:
12
+ """Send Instagram text DMs using an Instagram Graph API access token."""
13
+
14
+ def __init__(self, access_token: str | None = None, *, live: bool | None = None):
15
+ self.access_token = access_token or os.getenv("INSTAGRAM_GRAPH_API_ACCESS_TOKEN")
16
+ self.live = (
17
+ live if live is not None else
18
+ os.getenv("IS_REALLY_SEND_INSTAGRAM_MESSAGE", "false").lower() in {"1", "true", "yes"}
19
+ )
20
+
21
+ def send_direct_message(self, *, recipient_id: str, text: str) -> str | None:
22
+ if not recipient_id:
23
+ raise ValueError("recipient_id is required")
24
+ if not text:
25
+ raise ValueError("text is required")
26
+ if not self.live:
27
+ return None
28
+ if not self.access_token:
29
+ raise ValueError("INSTAGRAM_GRAPH_API_ACCESS_TOKEN is required for live requests")
30
+
31
+ response = requests.post(
32
+ "https://graph.instagram.com/v19.0/me/messages",
33
+ headers={"Authorization": f"Bearer {self.access_token}"},
34
+ json={"recipient": {"id": recipient_id}, "message": {"text": text}},
35
+ timeout=30,
36
+ )
37
+ if response.status_code != HTTPStatus.OK:
38
+ response.raise_for_status()
39
+ return response.json().get("message_id")
@@ -0,0 +1,6 @@
1
+ import enum
2
+
3
+
4
+ class TheExample(enum.Enum):
5
+ VALUE1_ID = 20
6
+ VALUE2_ID = 18
@@ -0,0 +1,27 @@
1
+ from python_sdk_remote.our_object import OurObject
2
+
3
+ from .instagram_message_api_client import InstagramMessageApiClient
4
+
5
+ ENTITY_NAME = "InstagramMessageApi"
6
+
7
+
8
+ class InstagramMessageLocal(OurObject):
9
+
10
+ # TODO Add all fields
11
+ fields = {
12
+ "<example>_id",
13
+ "display_as",
14
+ }
15
+
16
+ def __init__(self, entity_name=ENTITY_NAME, **kwargs):
17
+ super().__init__(entity_name, **kwargs)
18
+
19
+ # Mandatory pure virtual method from OurObject
20
+ def get_name(self):
21
+ print(f"{ENTITY_NAME} get_name() self.fields.display_as={self.fields.display_as}")
22
+ return self.fields.display_as
23
+
24
+ def send(self, *, recipient_id: str, text: str) -> str | None:
25
+ """Send a text direct message through the Instagram Graph API."""
26
+ return InstagramMessageApiClient().send_direct_message(
27
+ recipient_id=recipient_id, text=text)
@@ -0,0 +1 @@
1
+ PACKAGE_VERSION = "0.0.0"
@@ -0,0 +1,5 @@
1
+ """Shared test configuration.
2
+
3
+ The API client tests are intentionally offline and need no database.
4
+ Database integration tests should opt in to their own fixture explicitly.
5
+ """
@@ -0,0 +1,53 @@
1
+ from http import HTTPStatus
2
+ from unittest.mock import MagicMock, patch
3
+
4
+ import requests
5
+
6
+ import pytest
7
+
8
+ from instagram_message_api_local.src.instagram_message_api_client import InstagramMessageApiClient
9
+
10
+
11
+ def test_dry_run_does_not_call_graph_api():
12
+ with patch("instagram_message_api_local.src.instagram_message_api_client.requests.post") as post:
13
+ assert InstagramMessageApiClient(live=False).send_direct_message(
14
+ recipient_id="user-1", text="hello") is None
15
+ post.assert_not_called()
16
+
17
+
18
+ @pytest.mark.parametrize("recipient_id,text", [("", "hello"), ("user-1", "")])
19
+ def test_send_validates_required_fields(recipient_id, text):
20
+ with pytest.raises(ValueError):
21
+ InstagramMessageApiClient(live=False).send_direct_message(
22
+ recipient_id=recipient_id, text=text)
23
+
24
+
25
+ def test_live_send_requires_token():
26
+ with pytest.raises(ValueError, match="ACCESS_TOKEN"):
27
+ InstagramMessageApiClient(live=True).send_direct_message(
28
+ recipient_id="user-1", text="hello")
29
+
30
+
31
+ def test_live_send_returns_meta_message_id():
32
+ response = MagicMock(status_code=HTTPStatus.OK, json=lambda: {"message_id": "mid-1"})
33
+ with patch("instagram_message_api_local.src.instagram_message_api_client.requests.post",
34
+ return_value=response) as post:
35
+ result = InstagramMessageApiClient(access_token="token", live=True).send_direct_message(
36
+ recipient_id="user-1", text="hello")
37
+ assert result == "mid-1"
38
+ post.assert_called_once_with(
39
+ "https://graph.instagram.com/v19.0/me/messages",
40
+ headers={"Authorization": "Bearer token"},
41
+ json={"recipient": {"id": "user-1"}, "message": {"text": "hello"}},
42
+ timeout=30,
43
+ )
44
+
45
+
46
+ def test_live_send_raises_for_graph_error():
47
+ response = MagicMock(status_code=HTTPStatus.BAD_REQUEST)
48
+ response.raise_for_status.side_effect = requests.HTTPError("bad request")
49
+ with patch("instagram_message_api_local.src.instagram_message_api_client.requests.post",
50
+ return_value=response):
51
+ with pytest.raises(Exception):
52
+ InstagramMessageApiClient(access_token="token", live=True).send_direct_message(
53
+ recipient_id="user-1", text="hello")
@@ -0,0 +1,26 @@
1
+ Metadata-Version: 2.4
2
+ Name: instagram-message-api-local
3
+ Version: 0.0.3b4578
4
+ Summary: PyPI instagram-message-api-local Python Package owned by Circlez.ai
5
+ Home-page: https://github.com/circles-zone/instagram-message-api-local-python-package
6
+ Author: Circles
7
+ Author-email: info@circlez.ai
8
+ License: MIT
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Operating System :: OS Independent
11
+ Description-Content-Type: text/markdown
12
+ Requires-Dist: python-sdk-remote
13
+ Requires-Dist: requests
14
+ Dynamic: author
15
+ Dynamic: author-email
16
+ Dynamic: classifier
17
+ Dynamic: description
18
+ Dynamic: description-content-type
19
+ Dynamic: home-page
20
+ Dynamic: license
21
+ Dynamic: requires-dist
22
+ Dynamic: summary
23
+
24
+ PyPI instagram-message-api-local Python Package owned by Circlez.ai
25
+ JIRA Work Item: https://circles-zone.atlassian.net/browse/BU-2614
26
+ GHA: https://github.com/circles-zone/instagram-message-api-local-python-package/actions
@@ -0,0 +1,21 @@
1
+ README.md
2
+ pyproject.toml
3
+ setup.py
4
+ instagram_message_api_local/__init__.py
5
+ instagram_message_api_local.egg-info/PKG-INFO
6
+ instagram_message_api_local.egg-info/SOURCES.txt
7
+ instagram_message_api_local.egg-info/dependency_links.txt
8
+ instagram_message_api_local.egg-info/entry_points.txt
9
+ instagram_message_api_local.egg-info/requires.txt
10
+ instagram_message_api_local.egg-info/top_level.txt
11
+ instagram_message_api_local/src/__init__.py
12
+ instagram_message_api_local/src/constants_src_the_example_local.py
13
+ instagram_message_api_local/src/instagram_message_api_cli.py
14
+ instagram_message_api_local/src/instagram_message_api_client.py
15
+ instagram_message_api_local/src/instagram_message_api_enum.py
16
+ instagram_message_api_local/src/instagram_message_api_local.py
17
+ instagram_message_api_local/src/version.py
18
+ instagram_message_api_local/tests/__init__.py
19
+ instagram_message_api_local/tests/conftest.py
20
+ instagram_message_api_local/tests/unit/__init__.py
21
+ instagram_message_api_local/tests/unit/instagram_message_api_client_unit_test.py
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ instagram-message = instagram_message_api_local.src.instagram_message_api_cli:main
@@ -0,0 +1,30 @@
1
+ # This file should be in the future instead of setup.py
2
+ # https://python-poetry.org/docs/pyproject
3
+ # https://stackoverflow.com/questions/78048223/adding-folder-with-data-with-pyproject-toml
4
+ # https://packaging.python.org/en/latest/guides/writing-pyproject-toml/#license
5
+
6
+ # This file is mandatory for the `poetry version patch`
7
+
8
+ # It seems we need to copy this file also to serverless-com repo, as required by dialog-workflow-python-package
9
+
10
+ [build-system]
11
+ requires = ["setuptools>=61.0"]
12
+ build-backend = "setuptools.build_meta"
13
+
14
+ [tool.pytest.ini_options]
15
+ pythonpath = ["."]
16
+
17
+ [tool.poetry]
18
+ # TODO: Please update the name, similar to storage-local (suffix -local)
19
+ name = "<project-name>"
20
+ # I believe we are still using the version from setup.py and not from here until Potery will work
21
+ version = "0.0.3b4578" # https://pypi.org/project/<project-name> i.e. https://pypi.org/project/storage-local/
22
+ description = "<project-name> Python Package"
23
+ readme = "README.md"
24
+ authors = [
25
+ "Circlez.ai <info@circlez.ai>",
26
+ ]
27
+
28
+ [tool.poetry.dev-dependencies]
29
+ pytest = "^8.0"
30
+ pytest-cov = "^5.0"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,80 @@
1
+ # serverless.com repo "python -m build" works without setup.py
2
+ # Should update package_dir if not using our recommended directory structure
3
+
4
+ import setuptools
5
+
6
+ # TODO: Change the PACKAGE_NAME to the package's name - Either xxx-local or
7
+ # xxx-remote (without the -python-package suffix). Only lowercase, no
8
+ # underlines.
9
+ # Used by pypa/gh-action-pypi-publish
10
+ # Package Name should be identical to the inner directory name
11
+ # Changing the package name here, will cause a change in the package directory
12
+ # name as well
13
+ # PACKAGE_NAME should be singular if handling only one instance
14
+ # PACKAGE_NAME should not include the word "main"
15
+
16
+ # e.g.: queue-local, without python-package suffix
17
+ PACKAGE_NAME = "instagram-message-api-local"
18
+
19
+ package_dir = PACKAGE_NAME.replace("-", "_")
20
+ # If we need backward-compatible:
21
+ # old_package_dir = "old_package_name"
22
+
23
+ PACKAGE_DESCRIPTION = f"PyPI {PACKAGE_NAME} Python Package owned by Circlez.ai"
24
+
25
+ setuptools.setup(
26
+ license='MIT',
27
+ name=PACKAGE_NAME,
28
+ # Increase this number every time you make a change you
29
+ # want to publish. After 0.0.9 switch to 0.0.10 and not 0.1.0
30
+ # version can't be 0.0.
31
+ # If you find yourself increasing the version in more than one patch number (it might indicate there are multiple branches which you should submit their PRs before your PR) # noqa: E501
32
+ # TODO add the Jira Work Item (Jira Issue) number after the version i.e., 0.0.1b2432
33
+ version="0.0.3b4578", # https://pypi.org/project/instagram-message-api-local/
34
+ author="Circles",
35
+ author_email="info@circlez.ai",
36
+ description=f"{PACKAGE_DESCRIPTION}",
37
+ # long_description=f"{description}",
38
+ long_description=f"{PACKAGE_DESCRIPTION}\n"
39
+ "JIRA Work Item: https://circles-zone.atlassian.net/browse/BU-2614\n"
40
+ "GHA: https://github.com/circles-zone/instagram-message-api-local-python-package/actions",
41
+
42
+ long_description_content_type='text/markdown',
43
+ url=f"https://github.com/circles-zone/{PACKAGE_NAME}-python-package",
44
+ # packages=[package_dir],
45
+ # packages=[package_dir, old_package_dir],
46
+ packages=setuptools.find_packages(),
47
+ entry_points={
48
+ "console_scripts": [
49
+ "instagram-message=instagram_message_api_local.src.instagram_message_api_cli:main",
50
+ ],
51
+ },
52
+
53
+ # I'm not sure we need package_dir
54
+ # package_dir={package_dir: f'{package_dir}/src'},
55
+ # TODO Unfortunately in event-main-local-restapi there are no
56
+ # repo-directory and no package directory (flat directory structure)
57
+ # package_dir={package_dir: f'src'},
58
+
59
+ # package_dir={package_dir: f'{package_dir}/src',
60
+ # old_package_dir: f'{package_dir}/src'},
61
+ package_data={package_dir: ['*.py']},
62
+ classifiers=[
63
+ "Programming Language :: Python :: 3",
64
+ # https://packaging.python.org/en/latest/guides/writing-pyproject-toml/#license
65
+ # TODO Fix all repos, especially the Python templates
66
+ #
67
+ "Operating System :: OS Independent",
68
+ ],
69
+ # Do not add packages needed only for tests i.e. packages for get_test_xxx(
70
+ # TODO: Update which packages to include with this package in production
71
+ # (dependencies) - Not for development/testing
72
+ install_requires=[
73
+ 'python-sdk-remote',
74
+ # TODO: In -local package please uncomment the bellow line. In -remote packages please delete this line. # noqa E501
75
+ # 'smart-datastore-local',
76
+ # 'logzio-python-handler>=4.1.2', # https://pypi.org/project/logzio-python-handler/
77
+ 'requests',
78
+ ]
79
+ )
80
+