Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions newapi/__init__.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
""" """

from .api_utils.lang_codes import change_codes

from . import page
from .all_apis import AllAPIS
from .api_client.client import WikiLoginClient
from .api_utils import botEdit, txtlib, wd_sparql
from .client_wiki.api_utils import botEdit, txtlib, wd_sparql
from .client_wiki.api_utils.lang_codes import change_codes
from .DB_bots import db_bot, pymysql_bot

__all__ = [
Expand Down
2 changes: 1 addition & 1 deletion newapi/all_apis.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
""" """

from .pages_bots.all_apis import (
from .client_wiki.all_apis import (
AllAPIS,
)

Expand Down
203 changes: 109 additions & 94 deletions newapi/api_client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -485,29 +485,16 @@ def site(self) -> mwclient.Site:
"""The underlying ``mwclient.Site`` — use for high-level wiki access."""
return self._site

# ── Public methods ─────────────────────────────────────────────────────

def login(self, force: bool = False) -> None:
"""
Force a fresh login regardless of cookie state.

Call this if you know the session has expired and want to re-authenticate
without creating a new WikiLoginClient instance.
"""
if force or not self._site.logged_in:
logger.info(
"Forcing re-login for %s on %s.%s",
self.username,
self.lang,
self.family,
)
self._do_login()
# ------------------------------------------------------------------
# Private helpers
# ------------------------------------------------------------------

def _client_request(
self,
params: dict,
method: str = "post",
files: Optional[Any] = None,
**kwargs,
) -> dict:
"""
Send a GET or POST request to the wiki API and return parsed JSON.
Expand Down Expand Up @@ -553,43 +540,135 @@ def _client_request(
action = params.get("action")
if action in self._WRITE_ACTIONS:
method = "post"

if method == "get":
return self._request_with_retry("GET", self.api_url, params=params)
#return self._site.get(action, **params)
# return self._site.get(action, **params)
else:
# Fetch a CSRF token now if the caller didn't supply one.
# The retry loop will refresh it automatically on CSRF errors.
if "token" not in params:
params["token"] = self._site.get_token("csrf")

return self._request_with_retry("POST", self.api_url, data=params, files=files)
#return self._site.post(action, **params, files=files)
# return self._site.post(action, **params, files=files)

def _ensure_logged_in(self) -> None:
"""
Check whether the current session is authenticated.
"""
# if self._site.logged_in:
if getattr(self._site, "logged_in", None):
logger.info(f"Session already authenticated {self._site.logged_in=}")
return
if self._cookie_path.exists():
try:
self._site.site_init()
if self._site.logged_in:
logger.info("Revived session via cookies as %s", self._site.username)
return
except Exception:
logger.exception("Error in site_init")

# if not self._site.logged_in: self._do_login()
# don't login yet, user can use login() method

def _enrich_params(self, params: dict) -> dict:
"""
Inject write-action safety parameters.

For write actions:
- ``bot=1`` marks edits as bot edits in recent changes.
- ``assertuser`` ensures the API rejects requests from the wrong
account (guards against accidental edits).

Query actions have write-only keys scrubbed instead.
"""
params = dict(params)
action = params.get("action", "")

# Strip write-only params from query actions
if action == "query":
params.pop("bot", None)
params.pop("summary", None)
return params

# Inject bot marker and identity assertion for all write actions
is_write = action in self._WRITE_ACTIONS or action.startswith("wb") or self.family == "wikidata"
if is_write and self.username:
params.setdefault("bot", 1)
params.setdefault("assertuser", self.username)

return params

def _do_login(self) -> None:
"""
Execute the mwclient login handshake and persist the resulting cookies.

Raises:
LoginError: if mwclient rejects the credentials.
"""
try:
self._site.login(self.username, self._password)
except mwclient.errors.LoginError as exc:
raise LoginError(f"login failed for {self.username} on {self.lang}.{self.family}: {exc}") from exc

if self._site.logged_in:
logger.info(
"Logged in successfully as %s on %s.%s",
self.username,
self.lang,
self.family,
)
self.save_cookies(self.cj)

# ── Public methods ─────────────────────────────────────────────────────

def login(self, force: bool = False) -> None:
"""
Force a fresh login regardless of cookie state.

Call this if you know the session has expired and want to re-authenticate
without creating a new WikiLoginClient instance.
"""
if force or not self._site.logged_in:
logger.info(
"Forcing re-login for %s on %s.%s",
self.username,
self.lang,
self.family,
)
self._do_login()

def client_request(
self,
params: dict,
method: str = "post",
files: Optional[Any] = None,
**kwargs,
) -> dict:
""" """
return self._client_request(
params=params,
method=method,
files=files,
**kwargs,
)

def client_request_safe(
self,
params: dict,
method: str = "post",
files: Optional[Any] = None,
**kwargs,
) -> dict:
""" """
try:
return self._client_request(
params=params,
method=method,
files=files,
**kwargs,
)
except Exception as exc:
logger.warning("client_request_safe: %s", exc)
Expand All @@ -600,6 +679,7 @@ def client_request_retry(
params: dict,
method: str = "post",
files: Optional[Any] = None,
**kwargs,
) -> dict:
"""
Send a GET or POST request to the wiki API and return parsed JSON.
Expand Down Expand Up @@ -628,18 +708,25 @@ def client_request_retry(
raise ValueError(f"method must be 'get' or 'post', got {method!r}")

# Files can only travel via multipart POST
if files is not None:
action = params.get("action")
if action in self._WRITE_ACTIONS or files is not None:
method = "post"

# Always request JSON and inject write-action safety params
params = self._enrich_params({"format": "json", **params})

skip_log_params = [
"token",
"password",
"lgpassword",
"text",
]
logger.debug(
"%s %s params=%s files=%s",
method.upper(),
self.api_url,
# Never log token values
{k: ("***" if k == "token" else v) for k, v in params.items()},
{k: ("***" if k in skip_log_params else v) for k, v in params.items()},
list(files.keys()) if files else None,
)

Expand Down Expand Up @@ -672,6 +759,7 @@ def post_continue(
first: bool = False,
_p_2: str = "",
_p_2_empty: Optional[Union[list, dict]] = None,
**kwargs,
) -> Union[list, dict]:
"""
Drive a MediaWiki API continuation query to completion.
Expand Down Expand Up @@ -755,79 +843,6 @@ def post_continue(
logger.debug("done, %d total results", len(results))
return results

# ------------------------------------------------------------------
# Private helpers
# ------------------------------------------------------------------

def _ensure_logged_in(self) -> None:
"""
Check whether the current session is authenticated.
"""
# if self._site.logged_in:
if getattr(self._site, "logged_in", None):
logger.info(f"Session already authenticated {self._site.logged_in=}")
return
if self._cookie_path.exists():
try:
self._site.site_init()
if self._site.logged_in:
logger.info("Revived session via cookies as %s", self._site.username)
return
except Exception:
logger.exception("Error in site_init")

# if not self._site.logged_in: self._do_login()
# don't login yet, user can use login() method

def _enrich_params(self, params: dict) -> dict:
"""
Inject write-action safety parameters.

For write actions:
- ``bot=1`` marks edits as bot edits in recent changes.
- ``assertuser`` ensures the API rejects requests from the wrong
account (guards against accidental edits).

Query actions have write-only keys scrubbed instead.
"""
params = dict(params)
action = params.get("action", "")

# Strip write-only params from query actions
if action == "query":
params.pop("bot", None)
params.pop("summary", None)
return params

# Inject bot marker and identity assertion for all write actions
is_write = action in self._WRITE_ACTIONS or action.startswith("wb") or self.family == "wikidata"
if is_write and self.username:
params.setdefault("bot", 1)
params.setdefault("assertuser", self.username)

return params

def _do_login(self) -> None:
"""
Execute the mwclient login handshake and persist the resulting cookies.

Raises:
LoginError: if mwclient rejects the credentials.
"""
try:
self._site.login(self.username, self._password)
except mwclient.errors.LoginError as exc:
raise LoginError(f"login failed for {self.username} on {self.lang}.{self.family}: {exc}") from exc

if self._site.logged_in:
logger.info(
"Logged in successfully as %s on %s.%s",
self.username,
self.lang,
self.family,
)
self.save_cookies(self.cj)

def __repr__(self) -> str:
return f"WikiLoginClient(lang={self.lang!r}, family={self.family!r}, username={self.username!r})"

Expand Down
File renamed without changes.
14 changes: 4 additions & 10 deletions newapi/pages_bots/all_apis.py → newapi/client_wiki/all_apis.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,11 @@
"""

main_api = AllAPIS(lang='en', family='wikipedia', username='your_username', password='your_password')
page = main_api.MainPage('Main Page Title')
cat_members = main_api.CatDepth('Category Title')
new_api = main_api.NewApi()
"""
""" """

import logging

from ..api_client.client import WikiLoginClient
from ..api_client import WikiLoginClient
from ..super.S_API import bot_api
from ..super.S_Category import catdepth_new
from ..super.S_Page import super_page
from .categories import catdepth_new
from .pages import super_page

logger = logging.getLogger(__name__)

Expand Down
30 changes: 30 additions & 0 deletions newapi/client_wiki/api_utils/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from .ask_bot import AskBot, showDiff
from .botEdit import (
bot_May_Edit,
check_create_time,
check_last_edit_time,
)

change_codes = {
"bat_smg": "bat-smg",
"be-x-old": "be-tarask",
"be_x_old": "be-tarask",
"cbk_zam": "cbk-zam",
"fiu_vro": "fiu-vro",
"map_bms": "map-bms",
"nb": "no",
"nds_nl": "nds-nl",
"roa_rup": "roa-rup",
"zh_classical": "zh-classical",
"zh_min_nan": "zh-min-nan",
"zh_yue": "zh-yue",
}

__all__ = [
"AskBot",
"change_codes",
"showDiff",
"bot_May_Edit",
"check_create_time",
"check_last_edit_time",
]
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@

import pywikibot

from ..config import settings
from ...config import settings

logger = logging.getLogger(__name__)

_save_or_ask: dict[str, bool] = {}


Expand Down
Loading
Loading