Skip to content

Instantly share code, notes, and snippets.

@zbycz
Created August 21, 2024 12:30
Show Gist options
  • Save zbycz/d93b35d6d6c7fcff6f118c2f4758ddab to your computer and use it in GitHub Desktop.
Save zbycz/d93b35d6d6c7fcff6f118c2f4758ddab to your computer and use it in GitHub Desktop.
Mediawiki API client with file upload (TypeScript)
import fetch from 'isomorphic-unfetch';
import {
FORMAT,
getUploadBody,
cookieJar,
UploadParams,
WIKI_URL,
} from './utils';
import { readFile } from 'node:fs/promises';
type Params = Record<string, string>;
export const getMediaWikiSession = () => {
let sessionCookie: string | undefined;
const GET = async (action: string, params: Params) => {
const query = new URLSearchParams({ action, ...params, ...FORMAT });
const response = await fetch(`${WIKI_URL}?${query}`, {
headers: { Cookie: sessionCookie },
});
sessionCookie = cookieJar(sessionCookie, response);
return response.json();
};
const POST = async (action: string, params: Params) => {
const query = new URLSearchParams({ action, ...params, ...FORMAT });
const response = await fetch(WIKI_URL, {
method: 'POST',
headers: { Cookie: sessionCookie },
body: query,
});
sessionCookie = cookieJar(sessionCookie, response);
return response.json();
};
const UPLOAD = async (action: string, params: UploadParams) => {
const response = await fetch(WIKI_URL, {
method: 'POST',
headers: { Cookie: sessionCookie },
body: getUploadBody({ action, ...params, ...FORMAT }),
});
sessionCookie = cookieJar(sessionCookie, response);
return response.json();
};
const getLoginToken = async (): Promise<string> => {
const data = await GET('query', { meta: 'tokens', type: 'login' });
return data.query.tokens.logintoken;
};
const login = async (lgname: string, lgpassword: string) => {
const lgtoken = await getLoginToken();
const data = await POST('login', { lgname, lgpassword, lgtoken });
return data.login;
};
const getCsrfToken = async () => {
const data = await GET('query', { meta: 'tokens', type: 'csrf' });
console.log('getCsrfToken', data.query.tokens.csrftoken);
return data.query.tokens.csrftoken;
};
const upload = async (filepath: string, filename: string, text: string) => {
const token = await getCsrfToken();
const file = await readFile(filepath);
const blob = new Blob([file], { type: 'application/octet-stream' });
const data = await UPLOAD('upload', {
file: blob,
filename,
text,
comment: 'Initial upload from OsmAPP.org',
token,
});
return data;
};
// https://github.com/multichill/toollabs/blob/master/bot/commons/geograph_uploader.py#L132
const editClaims = async (pageId: string, claims) => {
const token = await getCsrfToken();
const response = await POST('wbeditentity', {
id: pageId,
data: JSON.stringify({ claims }),
token,
});
return await response.json();
};
return {
login,
upload,
editClaims,
};
};
import { getMediaWikiSession } from './mediawiki';
import { readFile } from 'node:fs/promises';
(async () => {
const password = (await readFile('../../../../.env.local', 'utf-8'))
.split('\n')[0]
.split('=')[1];
const session = getMediaWikiSession();
console.log(await session.login('OsmappBot@osmapp-upload', password));
})();
export const WIKI_URL = 'https://test.wikipedia.org/w/api.php';
export const FORMAT = { format: 'json', formatversion: '2' };
export type UploadParams = Record<string, string | Blob>;
export const getUploadBody = (params: UploadParams) => {
const formData = new FormData();
Object.entries(params).forEach(([k, v]) => {
formData.append(k, v);
});
return formData;
};
export const cookieJar = (cookies: string, response: Response) => {
const oldCookies: Record<string, string> =
cookies?.split(';').reduce((acc, c) => {
const [name, value] = c.split('=');
return { ...acc, [name.trim()]: value.trim() };
}, {}) ?? {};
const headers = new Headers(response.headers);
headers.getSetCookie().forEach((cookie) => {
const [name, value] = cookie.split(';')[0].split('=');
oldCookies[name] = value;
});
const out = Object.entries(oldCookies)
.map(([name, value]) => `${name}=${value}`)
.join('; ');
return out;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment