Initial commit

- Implemented Login
This commit is contained in:
Oliver Hattshire 2025-10-23 19:17:24 -03:00
commit c0cf0bc40c
8 changed files with 185 additions and 0 deletions

5
.gitignore vendored Normal file
View file

@ -0,0 +1,5 @@
__pycache__/
.venv/
*.egg-info/
typings/
persistent/

14
Insthidge/__init__.py Normal file
View file

@ -0,0 +1,14 @@
from slidge import entrypoint
from . import gateway, session
def main():
entrypoint("Insthidge")
__all__ = (
"gateway",
"main",
"session",
)

3
Insthidge/__main__.py Normal file
View file

@ -0,0 +1,3 @@
from Insthidge import main
main()

71
Insthidge/gateway.py Normal file
View file

@ -0,0 +1,71 @@
import asyncio
from instagrapi import Client
from instagrapi.exceptions import (
BadPassword,
ChallengeRequired,
FeedbackRequired,
TwoFactorRequired,
)
from slidge import BaseGateway, GatewayUser
from slidge.command.register import RegistrationType, TwoFactorNotRequired
from slixmpp import JID
from .utils import get_session_file
class Gateway(BaseGateway):
COMPONENT_NAME = "Instagram (Slidge)"
COMPONENT_TYPE = "instagram"
COMPONENT_AVATAR = ("https://upload.wikimedia.org/wikipedia/commons/thumb/e/e7/"
"Instagram_logo_2016.svg/640px-Instagram_logo_2016.svg.png")
ROSTER_GROUP = "instagram"
REGISTRATION_INSTRUCTIONS = "Enter instagram credentials"
REGISTRATION_TYPE = RegistrationType.TWO_FACTOR_CODE
GROUPS = False
def __init__(self):
super().__init__()
self.instagram_client = dict[str, Client]()
async def validate(self, user_jid: JID, registration_form: dict[str, str | None]):
session_file = get_session_file(user_jid.bare)
client = Client()
try:
_ = await asyncio.to_thread(client.login,
registration_form["username"],
registration_form["password"]
)
except TwoFactorRequired:
self.instagram_client[user_jid.bare] = client
except BadPassword as e:
raise ValueError("Bad Password", e, e.args)
except ChallengeRequired as e:
raise ValueError("Browser Challenge Required", e, e.args)
except FeedbackRequired as e:
raise ValueError("Action moderated, account may be blocked", e, e.args)
except Exception as e:
raise ValueError("Could not authenticate: %s - %s", e, e.args)
else:
_ = await asyncio.to_thread(client.dump_settings, session_file)
raise TwoFactorNotRequired
async def validate_two_factor_code(self, user: GatewayUser, code: str):
session_file = get_session_file(user.jid.bare)
client = self.instagram_client[user.jid.bare]
try:
if await asyncio.to_thread(client.login,
user.legacy_module_data["username"],
user.legacy_module_data["password"],
verification_code=code):
if not await asyncio.to_thread(client.dump_settings, session_file):
raise IOError("Could not save session file: %s - %s", session_file)
except ChallengeRequired as e:
raise ValueError("Browser Challenge Required", e, e.args)
except FeedbackRequired as e:
raise ValueError("Action moderated, account may be blocked", e, e.args)
except Exception as e:
raise ValueError("Could not authenticate: %s - %s", e, e.args)

25
Insthidge/session.py Normal file
View file

@ -0,0 +1,25 @@
import asyncio
import instagrapi
from slidge import BaseSession
from .utils import get_session_file
class Session(BaseSession):
def __init__(self, *a, **kw):
super().__init__(*a, **kw)
async def login(self):
client = instagrapi.Client()
session_file = get_session_file(self.user_jid.bare)
client.load_settings(session_file)
await asyncio.to_thread(client.login,
self.user.legacy_module_data["username"],
self.user.legacy_module_data["password"])
self.log.info(
"Logged in: %s", str(self.user_jid.bare)
)

5
Insthidge/utils.py Normal file
View file

@ -0,0 +1,5 @@
from slidge import global_config
def get_session_file(user_bare_jid: str):
return str(global_config.HOME_DIR / user_bare_jid)

5
README.md Normal file
View file

@ -0,0 +1,5 @@
# Insthidge
A XMPP puppeteering gateway based on Slidge and Instagrapi
Chat with Instagram users wthout leaving XMPP.

57
pyproject.toml Normal file
View file

@ -0,0 +1,57 @@
[project]
name = "Insthidge"
description = "A XMPP puppeteering gateway based on Slidge and Instagrapi"
authors = [
{name = "Oliver Hattshire", email = "oliver@hattshire.dev"},
]
readme = "README.md"
license = "Unlicense"
classifiers = [
"Topic :: Internet :: XMPP",
]
requires-python = ">= 3.11"
keywords = ["xmpp", "chat", "instagram", "gateway", "bridge", "instant messaging"]
version = "0.0.0a1"
dependencies = [
"slidge>=0.3,<0.4",
"instagrapi>=2.2,<3",
]
[project.scripts]
Insthidge = "Insthidge:main"
[dependency-groups]
dev = [
"mypy>=1.14.1",
"ruff>=0.9.1",
]
[build-system]
requires = ["setuptools", "setuptools-scm"]
build-backend = "setuptools.build_meta"
[tool.setuptools.packages.find]
include = ["Insthidge"]
[tool.setuptools_scm]
[[tool.uv.index]]
name = "pypi"
url = "https://pypi.org/simple"
[tool.mypy]
files = ["Insthidge"]
check_untyped_defs = true
strict = false
[[tool.mypy.overrides]]
module = ["instagrapi.*"]
ignore_missing_imports = true
[tool.ruff]
line-length = 88
[tool.ruff.lint]
select = ["I"]