The DEVA Data Model for the Patreon DEVA.



  • I’m working on the DEVA model to interact with patreon and get user accounts, and I finally got a good working model with something that makes a usable packet, and I wanted to share the code I was working on with you.

    This is the first time I’ve made a DEVA Data Model Public and shared it with anyone. So for you who don’t code this is the gem of my program Indra. You see the Svarga import is the state machine that manages how all the DEVA communicate and their base functionality.

    Beyond this Data Model there is only a few layers left of development before you see how it all works. But the Data Model is important because it defines your Agent Functionality.

    You can see that a Deva is very functional with just interacting on patreon its’ just a matter of time to figure out the bridge and develop the functionality.

    In the Indra.ai spectrum DEVA stands for Deterministic Emergent Virtualization Algorithm and this Data Model facilitates that algorithm in coming to life when you join it with a suitable ontology and a functional state machine.

    "use strict";
    const Svarga = require('***');
    const {patreon} = require('patreon');
    
    // Create new svarga object
    const PATREON = new Svarga({
      me: {
        name: 'Patreon',
        prompt: {
          text: 'Patreon',
          color: 'white',
        },
        voice: {
          speech: 'Alex',
          speed: 1,
        },
        profile: {
          avatar: '',
          gender: 'M',
          describe: 'The Patreon Deva Agent',
        },
        translate(input) {
          return input.trim();
        },
        parse(input) {
          return input.trim();
        }
      },
      vars: {
        urls: {
          user: '/current_user',
          campaigns: '/current_user/campaigns',
          pledges: '/campaigns/:CID:/pledges',
        },
        profile: {
          user: {},
          campaign: {},
          pledges: [],
          users: [],
        }
      },
      deva: {},
      listeners: {
        'patreon:question'(packet) {
          packet.q.text = packet.q.text.replace(/^patreon/g, '').trim()
          const cmd = packet.q.text.split(' ')[0]
          this.methods.question(cmd).then(data => {
            packet.a = {
              bot: this.me,
              text: `Patreon ${cmd}`,
              data,
            };
            this.talk(`patreon:question:${packet.id}`, packet)
          }).catch(err => {
            this.talk('error', { type: 'patreon:question', err: err.toString(), packet })
          });
        },
      },
      modules: {
        patreon: false,
      },
      methods: {
        question(cmd) {
          return new Promise((resolve, reject) => {
            if (this.vars.profile[cmd]) return resolve(this.vars.profile[cmd]);
            return reject('Ask another question')
          });
        },
        profile(packet) {
          return this.vars.profile
        },
        user() {
          return new Promise((resolve, reject) => {
            this.modules.patreon(this.vars.urls.user).then(result => {
              // clean up return packet and resolve good data
              const {id, attributes} = result.rawJson.data;
              resolve({
                id,
                about: attributes.about,
                created: attributes.created,
                discord_id: attributes.discord_id,
                first_name: attributes.first_name,
                last_name: attributes.last_name,
                full_name: attributes.full_name,
                image_url: attributes.image_url,
                thumb_url: attributes.thumb_url,
                url: attributes.url,
              });
            }).catch(reject);
          });
        },
        campaign() {
          return new Promise((resolve, reject) => {
            this.modules.patreon(this.vars.urls.campaigns).then(result => {
              const { data, included } = result.rawJson;
              const { attributes, id, relationships } = data[0];
              resolve({
                id,
                name: attributes.creation_name,
                summary: attributes.summary,
                thumb: attributes.image_small_url,
                image: attributes.image_url,
                created: attributes.created_at,
                video_url: attributes.main_video_url,
                patron_count: attributes.patron_count,
                pledge_sum: attributes.pledge_sum,
                pledge_url: attributes.pledge_url,
                thanks_msg: attributes.thanks_msg,
                rewards: included.reduce((result, v) => {
                  v.attributes.id = v.id;
                  if (v.type === 'reward') result.push(v.attributes);
                  return result;
                }, []),
                goals: included.reduce((result, v) => {
                  v.attributes.id = v.id;
                  if (v.type === 'goal') result.push(v.attributes);
                  return result;
                }, []),
              });
            }).catch(reject)
          });
        },
        getPledges(uri) {
          return new Promise((resolve, reject) => {
            this.modules.patreon(uri).then(result => {
              const { data, included, links } = result.rawJson;
              included.forEach((v,i) => {
                const { attributes, id, relationships, type } = v;
                const dupe = this.vars.profile.pledges.find(v => v.id === id) ? true : false;
                if (type !== 'user' || dupe) return;
                const included = {
                  id,
                  first_name: attributes.first_name,
                  last_name: attributes.last_name,
                  name: attributes.full_name,
                  email: attributes.email,
                  image: attributes.image_url,
                  thumb: attributes.thumb_url,
                  twitter: attributes.twitter,
                  youtube: attributes.youtube,
                  facebook: attributes.facebook,
                  social: attributes.social_connections,
                  url: attributes.url,
                  campaign: relationships.campaign.data,
                };
                this.vars.profile.users.push(included);
              });
    
              data.forEach((v,i) => {
                const { attributes, id, relationships, type } = v;
                const { campaign, users } = this.vars.profile;
                const user = users.find(user => user.id == relationships.patron.data.id);
                const reward = campaign.rewards.find(v => relationships.reward.data && v.id == relationships.reward.data.id);
                if (type !== 'pledge' || !user || !reward) return;
                const pledge = {
                  id,
                  user,
                  reward,
                  amount_cents: attributes.amount_cents,
                  created_at: attributes.created_at,
                  declined_since: attributes.declined_since,
                }
                this.vars.profile.pledges.push(pledge);
              });
              resolve(links);
            }).catch(reject);
          });
        },
        pledges(uri) {
          const self = this;
          return new Promise((resolve, reject) => {
            function getPledges(link) {
              return self.methods.getPledges(link).then(links => {
                if (links.next) return getPledges(links.next.split('2/api')[1]);
                return resolve(self.vars.profile.pledges);
              }).catch(reject);
            }
            return getPledges(uri);
          });
        },
        get(packet) {
          // return this.methods[packet.method](packet.msg); // to make deva exposed to the world
          return 'Nothing to see here' // to keep deva private
        }
      },
      onInit() {
        this.modules.patreon = patreon(this.config.patreon.accessToken);
        this.methods.user().then(userData => {
          this.vars.profile.user = userData;
          return this.methods.campaign()
        }).then(campaignData => {
          this.vars.urls.pledges = this.vars.urls.pledges.replace(/:CID:/g, campaignData.id);
          this.vars.profile.campaign = campaignData;
          return this.methods.pledges(this.vars.urls.pledges);
        }).then(pledgeData => {
          this.vars.profile.pledges = pledgeData;
          this.talk('members:enroll', pledgeData);
          this.talk('ready', this.me);
        }).catch(err => {
          console.error(err)
          this.talk('error', {type: 'patreon', err})
        });
      },
    });
    module.exports = PATREON