I'm making an unnoficial API in Typescript that I'll start using on some of my projects. Here is the snippet for the profile editing I coded today (in case it helps you somehow):
public async sesc(): Promise {
this.requireAuthenticated();
const response = await this.api.get(
"https://bitcointalk.org/index.php?action=profile",
);
const $ = cheerio.load(await response.text());
const logoutUrl = $(
'td.maintab_back a[href*="index.php?action=logout;sesc="]',
).attr("href");
const sesc = logoutUrl?.match(/sesc=(.*)/);
if (sesc && sesc[1]) {
return sesc[1];
}
throw new Error("Could not get sesc");
}
public async getProfile(userId?: ProfileUserId): Promise {
if (!userId) {
this.requireAuthenticated();
}
const url = userId
? `https://bitcointalk.org/index.php?action=profile;u=${userId}`
: "https://bitcointalk.org/index.php?action=profile";
const response = await this.api.get(url);
const html = await response.text();
const $ = cheerio.load(html);
const name = getTableRowValue($, "Name:");
const posts = getTableRowValue($, "Posts:");
const activity = getTableRowValue($, "Activity:");
const merit = getTableRowValue($, "Merit:");
const position = getTableRowValue($, "Position:");
const profileUserId = $("a[href*=/index.php?action=merit;u=]").attr("href")
?.match(
/\/index\.php\?action=merit;u=(\d+)/,
)?.at(1);
return {
name,
userId: Number(profileUserId),
posts: Number(posts),
activity: Number(activity),
merit: Number(merit),
position,
};
}
public async getProfileSettings() {
this.requireAuthenticated();
const response = await this.api.get(
`https://bitcointalk.org/index.php?action=profile;sa=forumProfile`,
);
const html = await response.text();
const $ = cheerio.load(html);
const personalText = $(
"#creator > table input[name=personalText]",
).attr("value")!;
const birthYear = $("#creator > table input[name=bday3]").attr("value")!;
const birthMonth = $("#creator > table input[name=bday1]").attr("value")!;
const birthDay = $("#creator > table input[name=bday2]").attr("value")!;
const location = $("#creator > table input[name=location]").attr("value")!;
const gender = $([
...$(
"#creator > table select[name=gender] > option",
),
].find((option) => $(option).attr("selected"))).attr("value") ?? 0;
const ICQ = $("#creator > table input[name=ICQ]").attr("value")!;
const AIM = $("#creator > table input[name=AIM]").attr("value")!;
const MSN = $("#creator > table input[name=MSN]").attr("value")!;
const YIM = $("#creator > table input[name=YIM]").attr("value")!;
const signature = $("#creator > table textarea[name=signature]").text();
const websiteTitle = $("#creator > table input[name=websiteTitle]").attr(
"value",
)!;
const websiteUrl = $("#creator > table input[name=websiteUrl]").attr(
"value",
)!;
const skype = $("#creator > table input[name='default_options[CP1]']").attr(
"value",
)!;
const bitcoinAddress = $(
"#creator > table input[namr='default_options[addr]']",
).attr("value")!;
const otherContactInfo = $(
$("#creator > table input[name='default_options[CP11]']"),
).attr("value")!;
const showUntrustedFeedbackByDefault =
$("#creator > table input.check[name='default_options[show_untrusted]']")
.attr("checked") === "checked";
const showPatrolLink =
$("#creator > table input.check[name='default_options[showpatrol]']")
.attr("checked") === "checked";
const showPostCountByPosts =
$("#creator > table input.check[name='default_options[showpostcount]']")
.attr("checked") === "checked";
const disableAds =
$("#creator > table input.check[name='default_options[noads]']").attr(
"checked",
) === "checked";
const result = {
personalText,
birthYear: String(birthYear),
birthMonth: String(birthMonth),
birthDay: String(birthDay),
location,
gender: Number(gender),
ICQ,
AIM,
MSN,
YIM,
signature,
websiteTitle,
websiteUrl,
skype,
bitcoinAddress,
otherContactInfo,
showUntrustedFeedbackByDefault,
showPatrolLink,
showPostCountByPosts,
disableAds,
};
return result;
}
public async editProfile(data?: EditProfileData): Promise {
const currentSettings = await this.getProfileSettings();
const profile = await this.getProfile();
const sesc = await this.sesc();
const body = new FormData();
const fields: Record = {
personalText: data?.personalText ?? currentSettings.personalText,
bday3: String(currentSettings.birthYear),
bday2: String(currentSettings.birthDay),
bday1: String(currentSettings.birthMonth),
location: data?.location ?? currentSettings.location,
gender: currentSettings.gender,
ICQ: currentSettings.ICQ,
AIM: currentSettings.AIM,
MSN: currentSettings.MSN,
YIM: currentSettings.YIM,
signature: data?.signature ?? currentSettings.signature,
websiteTitle: data?.websiteTitle ?? currentSettings.websiteTitle,
websiteUrl: data?.websiteTitle ?? currentSettings.websiteUrl,
"default_options[CP1]": currentSettings.skype,
"default_options[addr]": data?.bitcoinAddress ??
currentSettings.bitcoinAddress,
"default_options[show_untrusted]": Number(
currentSettings.showUntrustedFeedbackByDefault,
),
"default_options[showpatrol]": Number(currentSettings.showPatrolLink),
"default_options[showpostcount]": Number(
currentSettings.showPostCountByPosts,
),
"default_options[CP11]": currentSettings.otherContactInfo,
"default_options[noads]": Number(currentSettings.disableAds),
sc: sesc,
userID: profile.userId,
sa: "forumProfile",
};
for (const field in fields) {
body.set(field, String(fields[field]));
}
await this.api.post(
"https://bitcointalk.org/index.php?action=profile2",
{
body,
},
);
return true;
}
Some stuff is missing because it's WIP, but you can still have an idea of how it works.