import 'dotenv/config'; import { Client, CredentialManager, ok } from '@atcute/client'; import * as TID from '@atcute/tid'; //You want to make sure this is always the same for the applications you are generating upsert tids for //If you use the same timestamp but a different clock id, you will get different tids const CLOCK_ID = 23; const logErrorAndThrow = (err) => { console.error(err); throw err; }; //Loading in env variables can ignore const handle = process.env.HANDLE; if (!handle) logErrorAndThrow('HANDLE is not set'); const appPassword = process.env.APP_PASSWORD; if (!appPassword) logErrorAndThrow('APP_PASSWORD is not set'); const pdsHost = process.env.PDS_HOST; if (!pdsHost) logErrorAndThrow('PDS_HOST is not set'); //Sets up the atcute client const manager = new CredentialManager({ service: pdsHost }); const rpc = new Client({ handler: manager }); // sign in with handle/email and password (or app password (please be an app password)) await manager.login({ identifier: handle, password: appPassword}); // The actual example starts here, everything else before is boilerplate //reversing your handle to make a fun example lexicon const reversedHandle = handle.split('.').reverse().map(word => word).join('.'); //An activity here is like a workout. running, walking, lifting weights, etc. let collection = `${reversedHandle}.feed.activity` //A list of activities that may be gotten from your phone or wherever, but you get the whole list everytime let activities = [] // Helper function to upload activities const uploadActivities = async (activities, handle, collection, rpc) => { for (const activity of activities) { //Creates that unique key from the startTime of the activity so we don't have duplicates let rKey = TID.create(activity.startTime.getTime() * 1000, CLOCK_ID); await ok(rpc.post('com.atproto.repo.putRecord', { input: { repo: handle, collection, rkey: rKey, record: activity, } })); console.log(`Uploaded activity with rkey: ${rKey}`); } } //I just finished a run, it's saved to my phone, now uploading it to the PDS activities.push({ $type: collection, type: 'run', startTime: new Date() }) console.log('You just finished a run. Uploading it to the PDS.'); //Upload my activities await uploadActivities(activities, handle, collection, rpc); //Going to generate the key here to show you can find the record from it easily if you have a date const rkey = TID.create(activities[0].startTime.getTime() * 1000, CLOCK_ID); const activityFromPDS = await ok(rpc.get('com.atproto.repo.getRecord', { params: { repo: handle, collection, rkey, } })); console.log(`The PDS shows you went on a ${activityFromPDS.value.type} at ${activityFromPDS.value.startTime.toLocaleString()}.`); //I decide I want to go on a walk later, so I add it to the list activities.push({ $type: collection, type: 'walk', startTime: new Date() }) console.log('You just finished a walk. Uploading it to the PDS.'); //upload the new activities so the newest one can sync await uploadActivities(activities, handle, collection, rpc); const listResult = await ok(rpc.get('com.atproto.repo.listRecords', { params: { repo: handle, collection, limit: 10, } })); console.log("Since you did an upsert you should only have 2 records even tho you uploaded 3.") console.log(`You have ${listResult.records.length} activities saved in the PDS.`); for (const recordIndex in listResult.records){ const record = listResult.records[recordIndex] console.log(`The PDS shows you went on a ${record.value.type} at ${record.value.startTime.toLocaleString()}.`); }