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: 5 additions & 0 deletions docs/Reddit.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Platform: Reddit

Note: Reddit is moving to a new platform - devvit
https://developers.reddit.com/app-registration

Documentation below is for the legacy api.

## Set up the platform

### Create a new App in your Reddit account
Expand Down
16 changes: 14 additions & 2 deletions src/mappers/PlatformMapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ export default class PlatformMapper extends AbstractMapper<PlatformDto> {
get: ["managePlatforms"],
set: ["managePlatforms"],
},
connected: {
type: "boolean",
label: "Connected",
get: ["managePlatforms"],
set: ["managePlatforms"],
},
// more fields from platform.settings
// added in mapper constructor
};
Expand Down Expand Up @@ -61,6 +67,9 @@ export default class PlatformMapper extends AbstractMapper<PlatformDto> {
case "active":
dto[field] = !!this.platform.active;
break;
case "connected":
dto[field] = !!this.platform.connected;
break;
case "model":
case "id":
case "user_id":
Expand Down Expand Up @@ -116,8 +125,10 @@ export default class PlatformMapper extends AbstractMapper<PlatformDto> {
if (fields.includes(field)) {
switch (field) {
case "active":
if (dto[field]) await this.user.addPlatform(this.platform.id);
else await this.user.removePlatform(this.platform.id);
this.platform.active = !!dto[field];
break;
case "connected":
this.platform.connected = !!dto[field];
break;
default: {
switch (this.mapping[field].type) {
Expand Down Expand Up @@ -154,6 +165,7 @@ export default class PlatformMapper extends AbstractMapper<PlatformDto> {
this.user.log.trace("Ignoring field: " + field);
}
}
await this.platform.save();
await this.user.data.save();
return true;
}
Expand Down
55 changes: 55 additions & 0 deletions src/models/Platform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import User from "./User.ts";
export default class Platform {
id: PlatformId = PlatformId.UNKNOWN;
active: boolean = false;
connected: boolean = false;
user: User;
cache: { [id: string]: Post } = {};
defaultBody: string = "Fairpost feed";
Expand Down Expand Up @@ -75,6 +76,60 @@ export default class Platform {
return "No tests implemented for " + this.id;
}

/**
* save
*
* Save the platform - this is only 'active'
* and 'connected', as loaded in the User object
*/
async save() {
this.user.log.trace(
"Platform",
`Save ${this.id} (${this.active}, ${this.connected})`,
);
const activeIds = this.user.data
.get("settings", "FEED_PLATFORMS", "")
.split(",");
if (this.active) {
if (!activeIds.includes(this.id)) {
activeIds.push(this.id);
this.user.data.set("settings", "FEED_PLATFORMS", activeIds.join(","));
this.user.addPlatform(this);
}
} else {
const index = activeIds.indexOf(this.id);
if (index !== -1) {
activeIds.splice(index, 1);
this.user.data.set("settings", "FEED_PLATFORMS", activeIds.join(","));
this.user.removePlatform(this);
}
}
const connectedIds = this.user.data
.get("settings", "FEED_CONNECTED", "")
.split(",");
if (this.connected) {
if (!connectedIds.includes(this.id)) {
connectedIds.push(this.id);
this.user.data.set(
"settings",
"FEED_CONNECTED",
connectedIds.join(","),
);
}
} else {
const index = connectedIds.indexOf(this.id);
if (index !== -1) {
connectedIds.splice(index, 1);
this.user.data.set(
"settings",
"FEED_CONNECTED",
connectedIds.join(","),
);
}
}
await this.user.data.save();
}

/**
* refresh
*
Expand Down
120 changes: 32 additions & 88 deletions src/models/User.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { basename } from "path";
import * as readline from "node:readline/promises";

import * as platformClasses from "../platforms/index.ts";
import { PlatformId } from "../platforms/index.ts";
Expand All @@ -13,7 +12,6 @@ import UserData from "./User/UserData.ts";
import UserFiles from "./User/UserFiles.ts";
import UserLog from "./User/UserLog.ts";
import UserMapper from "../mappers/UserMapper.ts";
import { FieldMapping } from "../types/index.ts";

/**
* User - represents one fairpost user
Expand Down Expand Up @@ -215,14 +213,20 @@ export default class User {
*/
private loadPlatforms(): void {
this.log.trace("User", "loadPlatforms");
const platformIds = this.data
const activeIds = this.data
.get("settings", "FEED_PLATFORMS", "")
.split(",");
const connectedIds = this.data
.get("settings", "FEED_CONNECTED", "")
.split(",");
Object.values(platformClasses).forEach((platformClass) => {
if (typeof platformClass === "function") {
if (platformIds.includes(platformClass.id())) {
if (activeIds.includes(platformClass.id())) {
const platform = new platformClass(this);
platform.active = true;
if (connectedIds.includes(platformClass.id())) {
platform.connected = true;
}
if (this.platforms === undefined) {
this.platforms = {};
}
Expand All @@ -247,10 +251,22 @@ export default class User {
return platform;
}

const activeIds = this.data
.get("settings", "FEED_PLATFORMS", "")
.split(",");
const connectedIds = this.data
.get("settings", "FEED_CONNECTED", "")
.split(",");
Object.values(platformClasses).forEach((platformClass) => {
if (typeof platformClass === "function") {
if (platformClass.id() === platformId) {
platform = new platformClass(this);
if (activeIds.includes(platform.id)) {
platform.active = true;
}
if (connectedIds.includes(platform.id)) {
platform.connected = true;
}
}
}
});
Expand All @@ -276,96 +292,24 @@ export default class User {
}

/**
* Enable a platform on this user
* @param platformId
* @returns the enabled platform
*/
public async addPlatform(platformId: PlatformId): Promise<Platform> {
this.log.trace("User", "addPlatform", platformId);
if (
Object.values(PlatformId).includes(platformId) &&
platformId != PlatformId.UNKNOWN
) {
const platforms = this.data.get("settings", "FEED_PLATFORMS", "");
const platformIds = platforms ? platforms.split(",") : [];
if (!platformIds.includes(platformId)) {
platformIds.push(platformId);
this.data.set("settings", "FEED_PLATFORMS", platformIds.join(","));
await this.data.save();
}
this.loadPlatforms();
this.log.info(`Platform ${platformId} enabled for user ${this.id}`);
} else {
throw this.log.error("addPlatform: no such platform", platformId);
}
return this.getPlatform(platformId);
}

/**
* Disable a platform on this user
* @param platformId
* Add one platform on this users platforms[] after it has been set
* active. Does not save.
* @param platform
*/
public async removePlatform(platformId: PlatformId): Promise<void> {
this.log.trace("User", "removePlatforms", platformId);
if (
Object.values(PlatformId).includes(platformId) &&
platformId != PlatformId.UNKNOWN
) {
const platforms = this.data.get("settings", "FEED_PLATFORMS", "");
const platformIds = platforms ? platforms.split(",") : [];
const index = platformIds.indexOf(platformId);
if (index !== -1) {
platformIds.splice(index, 1);
this.data.set("settings", "FEED_PLATFORMS", platformIds.join(","));
await this.data.save();
}
this.loadPlatforms();
this.log.info(`Platform ${platformId} disabled for user ${this.id}`);
} else {
throw this.log.error("removePlatform: no such platform", platformId);
public addPlatform(platform: Platform) {
if (this.platforms && platform.active) {
this.platforms[platform.id] = platform;
}
}

/**
* @returns all data from the settings store

public getSettings(): { [key: string]: string } {
return this.data.getStore("settings");
}
* Remove one platform on this users platforms[] after it has been set
* inactive. Does not save.
* @param platform
*/

public async promptCliFields(
fields: FieldMapping,
): Promise<{ [key: string]: string }> {
const settings = {} as { [key: string]: string };
const reader = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
for (const key in fields) {
const current = this.data.get(
"settings",
key,
String(fields[key].default ?? ""),
);
const value =
(await reader.question(`${fields[key].label} ( ${current} ): `)) ||
current;
settings[key] = value;
public removePlatform(platform: Platform) {
if (this.platforms && !platform.active) {
delete this.platforms[platform.id];
}
reader.close();
return settings;
}

/**
* Update settings with values from payload
* @param payload - key/value object to save under settings store

public async putSettings(payload: { [key: string]: string }): Promise<void> {
for (const key in payload) {
this.data.set("settings", key, payload[key]);
}
await this.data.save();
}
*/
}
10 changes: 8 additions & 2 deletions src/platforms/Bluesky/Bluesky.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,20 @@ export default class Bluesky extends Platform {
async connect(operator: Operator, payload?: object) {
if (operator.ui === "cli") {
await this.auth.connectCli();
return await this.test();
const test = await this.test();
this.connected = true;
await this.save();
return test;
}
if (operator.ui === "api") {
if (!payload) {
throw this.user.log.error("Bluesky connect requires a payload");
}
await this.auth.connectApi(payload);
return await this.test();
const test = await this.test();
this.connected = true;
await this.save();
return test;
}
throw this.user.log.error(
`${this.id} connect: ui ${operator.ui} not supported`,
Expand Down
23 changes: 18 additions & 5 deletions src/platforms/Facebook/Facebook.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { basename } from "path";
import { FileGroup, FieldMapping } from "../../types/index.ts";
import { FileGroup, FieldMapping, OAuthRequest } from "../../types/index.ts";

import Source from "../../models/Source.ts";

Expand Down Expand Up @@ -70,14 +70,27 @@ export default class Facebook extends Platform {
async connect(operator: Operator, payload?: object) {
if (operator.ui === "cli") {
await this.auth.connectCli();
return await this.test();
const test = await this.test();
this.connected = true;
await this.save();
return test;
}

if (operator.ui === "api") {
if (!payload) {
throw this.user.log.error("Connect via api requires a payload");
const oauthPayload = payload as OAuthRequest;
const oauthResponse = await this.auth.connectApi(oauthPayload);
if (oauthResponse.phase !== "finish") {
return oauthResponse;
}
if (oauthResponse.authenticated) {
oauthResponse.results = await this.test();
this.connected = true;
await this.save();
return oauthResponse;
}
return this.auth.connectApi(payload);
return oauthResponse;
}

throw this.user.log.error(
`${this.id} connect: ui ${operator.ui} not supported`,
);
Expand Down
Loading
Loading