alicezetion 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cache/replit/__replit_disk_meta.json +1 -0
- package/.cache/replit/modules.stamp +0 -0
- package/.cache/replit/nix/env.json +1 -0
- package/.travis.yml +6 -0
- package/README.md +40 -0
- package/alice/add.js +99 -0
- package/alice/admin.js +65 -0
- package/alice/archive.js +41 -0
- package/alice/block.js +72 -0
- package/alice/chat.js +415 -0
- package/alice/color.js +53 -0
- package/alice/deletegc.js +43 -0
- package/alice/deletemsg.js +43 -0
- package/alice/delivered.js +41 -0
- package/alice/emoji.js +41 -0
- package/alice/emojiurl.js +29 -0
- package/alice/forward.js +47 -0
- package/alice/friend.js +70 -0
- package/alice/gchistorydeprecated.js +76 -0
- package/alice/gcimage.js +115 -0
- package/alice/gcimg.js +66 -0
- package/alice/gcinfo.js +170 -0
- package/alice/gcinfodeprecated.js +65 -0
- package/alice/gclist.js +220 -0
- package/alice/gclistdeprecated.js +75 -0
- package/alice/gcolor.js +22 -0
- package/alice/gcsearch.js +39 -0
- package/alice/history.js +632 -0
- package/alice/id.js +7 -0
- package/alice/kick.js +65 -0
- package/alice/listen.js +553 -0
- package/alice/listenMqtt.js +560 -0
- package/alice/logout.js +59 -0
- package/alice/msgrequest.js +51 -0
- package/alice/mute.js +38 -0
- package/alice/nickname.js +44 -0
- package/alice/poll.js +55 -0
- package/alice/react.js +82 -0
- package/alice/read.js +52 -0
- package/alice/resolveimgurl.js +31 -0
- package/alice/seen.js +36 -0
- package/alice/title.js +73 -0
- package/alice/typeindicator.js +77 -0
- package/alice/unsend.js +35 -0
- package/alice/userid.js +52 -0
- package/alice/userinfo.js +57 -0
- package/index.js +423 -0
- package/package.json +70 -0
- package/utils.js +1283 -0
| @@ -0,0 +1,560 @@ | |
| 1 | 
            +
            /* eslint-disable no-redeclare */
         | 
| 2 | 
            +
            "use strict";
         | 
| 3 | 
            +
            var utils = require("../utils");
         | 
| 4 | 
            +
            var log = require("npmlog");
         | 
| 5 | 
            +
            var mqtt = require('mqtt');
         | 
| 6 | 
            +
            var websocket = require('websocket-stream');
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            var identity = function () {};
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            //Don't really know what this does but I think it's for the active state
         | 
| 11 | 
            +
            //TODO: Move to ctx when implemented
         | 
| 12 | 
            +
            var chatOn = true;
         | 
| 13 | 
            +
            var foreground = false;
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            var topics = [
         | 
| 16 | 
            +
              "/t_ms",
         | 
| 17 | 
            +
              "/thread_typing",
         | 
| 18 | 
            +
              "/orca_typing_notifications",
         | 
| 19 | 
            +
              "/orca_presence",
         | 
| 20 | 
            +
              "/legacy_web",
         | 
| 21 | 
            +
              "/br_sr",
         | 
| 22 | 
            +
              "/sr_res",
         | 
| 23 | 
            +
              "/webrtc",
         | 
| 24 | 
            +
              "/onevc",
         | 
| 25 | 
            +
              "/notify_disconnect",
         | 
| 26 | 
            +
              "/inbox",
         | 
| 27 | 
            +
              "/mercury",
         | 
| 28 | 
            +
              "/messaging_events",
         | 
| 29 | 
            +
              "/orca_message_notifications",
         | 
| 30 | 
            +
              "/pp",
         | 
| 31 | 
            +
              "/webrtc_response",
         | 
| 32 | 
            +
            ];
         | 
| 33 | 
            +
             | 
| 34 | 
            +
            function listenMqtt(defaultFuncs, bot, ctx, globalCallback) {
         | 
| 35 | 
            +
              var sessionID = Math.floor(Math.random() * 9007199254740991) + 1;
         | 
| 36 | 
            +
              var username = {
         | 
| 37 | 
            +
                u: ctx.userID,
         | 
| 38 | 
            +
                s: sessionID,
         | 
| 39 | 
            +
                chat_on: chatOn,
         | 
| 40 | 
            +
                fg: foreground,
         | 
| 41 | 
            +
                d: utils.getGUID(),
         | 
| 42 | 
            +
                ct: "websocket",
         | 
| 43 | 
            +
                //App id from facebook
         | 
| 44 | 
            +
                aid: "219994525426954",
         | 
| 45 | 
            +
                mqtt_sid: "",
         | 
| 46 | 
            +
                cp: 3,
         | 
| 47 | 
            +
                ecp: 10,
         | 
| 48 | 
            +
                st: topics,
         | 
| 49 | 
            +
                pm: [],
         | 
| 50 | 
            +
                dc: "",
         | 
| 51 | 
            +
                no_auto_fg: true,
         | 
| 52 | 
            +
                gas: null
         | 
| 53 | 
            +
              };
         | 
| 54 | 
            +
              var cookies = ctx.jar.getCookies("https://www.facebook.com").join("; ");
         | 
| 55 | 
            +
             | 
| 56 | 
            +
              //Region could be changed for better ping. (Region atn: Southeast Asia, region ash: West US, prob) (Don't really know if we need it).
         | 
| 57 | 
            +
              //// var host = 'wss://edge-chat.facebook.com/chat?region=atn&sid=' + sessionID;
         | 
| 58 | 
            +
              var host = 'wss://edge-chat.facebook.com/chat?sid=' + sessionID;
         | 
| 59 | 
            +
             | 
| 60 | 
            +
              var options = {
         | 
| 61 | 
            +
                clientId: "mqttwsclient",
         | 
| 62 | 
            +
                protocolId: 'MQIsdp',
         | 
| 63 | 
            +
                protocolVersion: 3,
         | 
| 64 | 
            +
                username: JSON.stringify(username),
         | 
| 65 | 
            +
                clean: true,
         | 
| 66 | 
            +
                wsOptions: {
         | 
| 67 | 
            +
                  headers: {
         | 
| 68 | 
            +
                    'Cookie': cookies,
         | 
| 69 | 
            +
                    'Origin': 'https://www.facebook.com',
         | 
| 70 | 
            +
                    'User-Agent': ctx.globalOptions.userAgent,
         | 
| 71 | 
            +
                    'Referer': 'https://www.facebook.com',
         | 
| 72 | 
            +
                    'Host': 'edge-chat.facebook.com'
         | 
| 73 | 
            +
                  },
         | 
| 74 | 
            +
                  origin: 'https://www.facebook.com',
         | 
| 75 | 
            +
                  protocolVersion: 13
         | 
| 76 | 
            +
                }
         | 
| 77 | 
            +
              };
         | 
| 78 | 
            +
             | 
| 79 | 
            +
              ctx.mqttClient = new mqtt.Client(_ => websocket(host, options.wsOptions), options);
         | 
| 80 | 
            +
             | 
| 81 | 
            +
              var mqttClient = ctx.mqttClient;
         | 
| 82 | 
            +
             | 
| 83 | 
            +
              mqttClient.on('error', function(err) {
         | 
| 84 | 
            +
                log.error(err);
         | 
| 85 | 
            +
                mqttClient.end();
         | 
| 86 | 
            +
                globalCallback("Connection refused: Server unavailable", null);
         | 
| 87 | 
            +
              });
         | 
| 88 | 
            +
             | 
| 89 | 
            +
              mqttClient.on('connect', function() {
         | 
| 90 | 
            +
                var topic;
         | 
| 91 | 
            +
                var queue = {
         | 
| 92 | 
            +
                  sync_api_version: 10,
         | 
| 93 | 
            +
                  max_deltas_able_to_process: 1000,
         | 
| 94 | 
            +
                  delta_batch_size: 500,
         | 
| 95 | 
            +
                  encoding: "JSON",
         | 
| 96 | 
            +
                  entity_fbid: ctx.userID,
         | 
| 97 | 
            +
                };
         | 
| 98 | 
            +
             | 
| 99 | 
            +
                if(ctx.globalOptions.pageID) {
         | 
| 100 | 
            +
                  queue.entity_fbid = ctx.globalOptions.pageID;
         | 
| 101 | 
            +
                }
         | 
| 102 | 
            +
             | 
| 103 | 
            +
                if(ctx.syncToken) {
         | 
| 104 | 
            +
                  topic = "/messenger_sync_get_diffs";
         | 
| 105 | 
            +
                  queue.last_seq_id = ctx.lastSeqId;
         | 
| 106 | 
            +
                  queue.sync_token = ctx.syncToken;
         | 
| 107 | 
            +
                } else {
         | 
| 108 | 
            +
                  topic = "/messenger_sync_create_queue";
         | 
| 109 | 
            +
                  queue.initial_titan_sequence_id = ctx.lastSeqId;
         | 
| 110 | 
            +
                  queue.device_params = null;
         | 
| 111 | 
            +
                }
         | 
| 112 | 
            +
                
         | 
| 113 | 
            +
                mqttClient.publish(topic, JSON.stringify(queue), {qos: 1, retain: false});
         | 
| 114 | 
            +
              });
         | 
| 115 | 
            +
             | 
| 116 | 
            +
              mqttClient.on('message', function(topic, message, packet) {
         | 
| 117 | 
            +
                var jsonMessage = JSON.parse(message);
         | 
| 118 | 
            +
                if(topic === "/t_ms") {
         | 
| 119 | 
            +
                  if(jsonMessage.firstDeltaSeqId && jsonMessage.syncToken) {
         | 
| 120 | 
            +
                    ctx.lastSeqId = jsonMessage.firstDeltaSeqId;
         | 
| 121 | 
            +
                    ctx.syncToken = jsonMessage.syncToken;
         | 
| 122 | 
            +
                  }
         | 
| 123 | 
            +
             | 
| 124 | 
            +
                  if(jsonMessage.lastIssuedSeqId) {
         | 
| 125 | 
            +
                    ctx.lastSeqId = parseInt(jsonMessage.lastIssuedSeqId);
         | 
| 126 | 
            +
                  }
         | 
| 127 | 
            +
             | 
| 128 | 
            +
                  if(jsonMessage.queueEntityId && ctx.globalOptions.pageID &&
         | 
| 129 | 
            +
                    ctx.globalOptions.pageID != jsonMessage.queueEntityId) {
         | 
| 130 | 
            +
                    return;
         | 
| 131 | 
            +
                  }
         | 
| 132 | 
            +
             | 
| 133 | 
            +
                  //If it contains more than 1 delta
         | 
| 134 | 
            +
                  for (var i in jsonMessage.deltas) {
         | 
| 135 | 
            +
                    var delta = jsonMessage.deltas[i];
         | 
| 136 | 
            +
                    parseDelta(defaultFuncs, api, ctx, globalCallback, { "delta": delta });
         | 
| 137 | 
            +
                  }
         | 
| 138 | 
            +
                } else if (topic === "/thread_typing" || topic === "/orca_typing_notifications") {
         | 
| 139 | 
            +
                  var typ = {
         | 
| 140 | 
            +
                    type: "typ",
         | 
| 141 | 
            +
                    isTyping: !!jsonMessage.state,
         | 
| 142 | 
            +
                    from: jsonMessage.sender_fbid.toString(),
         | 
| 143 | 
            +
                    threadID: utils.formatID((jsonMessage.thread || jsonMessage.sender_fbid).toString())
         | 
| 144 | 
            +
                  };
         | 
| 145 | 
            +
                  (function () { globalCallback(null, typ); })();
         | 
| 146 | 
            +
                } else if (topic === "/orca_presence") {
         | 
| 147 | 
            +
                  if (!ctx.globalOptions.updatePresence) {
         | 
| 148 | 
            +
                    for (var i in jsonMessage.list) {
         | 
| 149 | 
            +
                      var data = jsonMessage.list[i];
         | 
| 150 | 
            +
                      var userID = data["u"];
         | 
| 151 | 
            +
             | 
| 152 | 
            +
                      var presence = {
         | 
| 153 | 
            +
                        type: "presence",
         | 
| 154 | 
            +
                        userID: userID.toString(),
         | 
| 155 | 
            +
                        //Convert to ms
         | 
| 156 | 
            +
                        timestamp: data["l"] * 1000,
         | 
| 157 | 
            +
                        statuses: data["p"]
         | 
| 158 | 
            +
                      };
         | 
| 159 | 
            +
                      (function () { globalCallback(null, presence); })();
         | 
| 160 | 
            +
                    }
         | 
| 161 | 
            +
                  }
         | 
| 162 | 
            +
                }
         | 
| 163 | 
            +
             | 
| 164 | 
            +
              });
         | 
| 165 | 
            +
             | 
| 166 | 
            +
              mqttClient.on('close', function() {
         | 
| 167 | 
            +
                // client.end();
         | 
| 168 | 
            +
              });
         | 
| 169 | 
            +
            }
         | 
| 170 | 
            +
             | 
| 171 | 
            +
            function parseDelta(defaultFuncs, api, ctx, globalCallback, v) {
         | 
| 172 | 
            +
              if(v.delta.class == "NewMessage") {
         | 
| 173 | 
            +
                (function resolveAttachmentUrl(i) {
         | 
| 174 | 
            +
                  // sometimes, with sticker message in group, delta does not contain 'attachments' property.
         | 
| 175 | 
            +
                  if (v.delta.attachments && (i == v.delta.attachments.length)) {
         | 
| 176 | 
            +
                    var fmtMsg;
         | 
| 177 | 
            +
                    try {
         | 
| 178 | 
            +
                      fmtMsg = utils.formatDeltaMessage(v);
         | 
| 179 | 
            +
                    } catch (err) {
         | 
| 180 | 
            +
                      return globalCallback({
         | 
| 181 | 
            +
                        error: "Problem parsing message object. Please open an issue at https://github.com/Schmavery/facebook-chat-api/issues.",
         | 
| 182 | 
            +
                        detail: err,
         | 
| 183 | 
            +
                        res: v,
         | 
| 184 | 
            +
                        type: "parse_error"
         | 
| 185 | 
            +
                      });
         | 
| 186 | 
            +
                    }
         | 
| 187 | 
            +
                    if (fmtMsg) {
         | 
| 188 | 
            +
                      if (ctx.globalOptions.autoMarkDelivery) {
         | 
| 189 | 
            +
                        markDelivery(ctx, api, fmtMsg.threadID, fmtMsg.messageID);
         | 
| 190 | 
            +
                      }
         | 
| 191 | 
            +
                    }
         | 
| 192 | 
            +
                    return !ctx.globalOptions.selfListen &&
         | 
| 193 | 
            +
                      fmtMsg.senderID === ctx.userID ?
         | 
| 194 | 
            +
                      undefined :
         | 
| 195 | 
            +
                      (function () { globalCallback(null, fmtMsg); })();
         | 
| 196 | 
            +
                  } else {
         | 
| 197 | 
            +
                    if (
         | 
| 198 | 
            +
                      v.delta.attachments && (v.delta.attachments[i].mercury.attach_type == "photo")
         | 
| 199 | 
            +
                    ) {
         | 
| 200 | 
            +
                      api.resolvePhotoUrl(
         | 
| 201 | 
            +
                        v.delta.attachments[i].fbid,
         | 
| 202 | 
            +
                        (err, url) => {
         | 
| 203 | 
            +
                          if (!err)
         | 
| 204 | 
            +
                            v.delta.attachments[
         | 
| 205 | 
            +
                              i
         | 
| 206 | 
            +
                            ].mercury.metadata.url = url;
         | 
| 207 | 
            +
                          return resolveAttachmentUrl(i + 1);
         | 
| 208 | 
            +
                        }
         | 
| 209 | 
            +
                      );
         | 
| 210 | 
            +
                    } else {
         | 
| 211 | 
            +
                      return resolveAttachmentUrl(i + 1);
         | 
| 212 | 
            +
                    }
         | 
| 213 | 
            +
                  }
         | 
| 214 | 
            +
                })(0);
         | 
| 215 | 
            +
              }
         | 
| 216 | 
            +
             | 
| 217 | 
            +
              if (v.delta.class == "ClientPayload") {
         | 
| 218 | 
            +
                var clientPayload = utils.decodeClientPayload(
         | 
| 219 | 
            +
                  v.delta.payload
         | 
| 220 | 
            +
                );
         | 
| 221 | 
            +
                if (clientPayload && clientPayload.deltas) {
         | 
| 222 | 
            +
                  for (var i in clientPayload.deltas) {
         | 
| 223 | 
            +
                    var delta = clientPayload.deltas[i];
         | 
| 224 | 
            +
                    if (delta.deltaMessageReaction && !!ctx.globalOptions.listenEvents) {
         | 
| 225 | 
            +
                      (function () { globalCallback(null, {
         | 
| 226 | 
            +
                        type: "message_reaction",
         | 
| 227 | 
            +
                        threadID: (delta.deltaMessageReaction.threadKey
         | 
| 228 | 
            +
                          .threadFbId ?
         | 
| 229 | 
            +
                          delta.deltaMessageReaction.threadKey.threadFbId : delta.deltaMessageReaction.threadKey
         | 
| 230 | 
            +
                            .otherUserFbId).toString(),
         | 
| 231 | 
            +
                        messageID: delta.deltaMessageReaction.messageId,
         | 
| 232 | 
            +
                        reaction: delta.deltaMessageReaction.reaction,
         | 
| 233 | 
            +
                        senderID: delta.deltaMessageReaction.senderId.toString(),
         | 
| 234 | 
            +
                        userID: delta.deltaMessageReaction.userId.toString()
         | 
| 235 | 
            +
                      }); })();
         | 
| 236 | 
            +
                    } else if (delta.deltaRecallMessageData && !!ctx.globalOptions.listenEvents) {
         | 
| 237 | 
            +
                      (function () { globalCallback(null, {
         | 
| 238 | 
            +
                        type: "message_unsend",
         | 
| 239 | 
            +
                        threadID: (delta.deltaRecallMessageData.threadKey.threadFbId ?
         | 
| 240 | 
            +
                          delta.deltaRecallMessageData.threadKey.threadFbId : delta.deltaRecallMessageData.threadKey
         | 
| 241 | 
            +
                            .otherUserFbId).toString(),
         | 
| 242 | 
            +
                        messageID: delta.deltaRecallMessageData.messageID,
         | 
| 243 | 
            +
                        senderID: delta.deltaRecallMessageData.senderID.toString(),
         | 
| 244 | 
            +
                        deletionTimestamp: delta.deltaRecallMessageData.deletionTimestamp,
         | 
| 245 | 
            +
                        timestamp: delta.deltaRecallMessageData.timestamp
         | 
| 246 | 
            +
                      }); })();
         | 
| 247 | 
            +
                    } else if (delta.deltaMessageReply) {
         | 
| 248 | 
            +
                      //Mention block - #1
         | 
| 249 | 
            +
                      var mdata =
         | 
| 250 | 
            +
                        delta.deltaMessageReply.message === undefined ? [] :
         | 
| 251 | 
            +
                          delta.deltaMessageReply.message.data === undefined ? [] :
         | 
| 252 | 
            +
                            delta.deltaMessageReply.message.data.prng === undefined ? [] :
         | 
| 253 | 
            +
                              JSON.parse(delta.deltaMessageReply.message.data.prng);
         | 
| 254 | 
            +
                      var m_id = mdata.map(u => u.i);
         | 
| 255 | 
            +
                      var m_offset = mdata.map(u => u.o);
         | 
| 256 | 
            +
                      var m_length = mdata.map(u => u.l);
         | 
| 257 | 
            +
             | 
| 258 | 
            +
                      var mentions = {};
         | 
| 259 | 
            +
             | 
| 260 | 
            +
                      for (var i = 0; i < m_id.length; i++) {
         | 
| 261 | 
            +
                        mentions[m_id[i]] = (delta.deltaMessageReply.message.body || "").substring(
         | 
| 262 | 
            +
                          m_offset[i],
         | 
| 263 | 
            +
                          m_offset[i] + m_length[i]
         | 
| 264 | 
            +
                        );
         | 
| 265 | 
            +
                      }
         | 
| 266 | 
            +
                      //Mention block - 1#
         | 
| 267 | 
            +
                      var callbackToReturn = {
         | 
| 268 | 
            +
                        type: "message_reply",
         | 
| 269 | 
            +
                        threadID: (delta.deltaMessageReply.message.messageMetadata.threadKey.threadFbId ?
         | 
| 270 | 
            +
                          delta.deltaMessageReply.message.messageMetadata.threadKey.threadFbId : delta.deltaMessageReply.message.messageMetadata.threadKey
         | 
| 271 | 
            +
                            .otherUserFbId).toString(),
         | 
| 272 | 
            +
                        messageID: delta.deltaMessageReply.message.messageMetadata.messageId,
         | 
| 273 | 
            +
                        senderID: delta.deltaMessageReply.message.messageMetadata.actorFbId.toString(),
         | 
| 274 | 
            +
                        attachments: delta.deltaMessageReply.message.attachments.map(function (att) {
         | 
| 275 | 
            +
                          var mercury = JSON.parse(att.mercuryJSON);
         | 
| 276 | 
            +
                          Object.assign(att, mercury);
         | 
| 277 | 
            +
                          return att;
         | 
| 278 | 
            +
                        }).map(att => {
         | 
| 279 | 
            +
                          var x;
         | 
| 280 | 
            +
                          try {
         | 
| 281 | 
            +
                            x = utils._formatAttachment(att);
         | 
| 282 | 
            +
                          } catch (ex) {
         | 
| 283 | 
            +
                            x = att;
         | 
| 284 | 
            +
                            x.error = ex;
         | 
| 285 | 
            +
                            x.type = "unknown";
         | 
| 286 | 
            +
                          }
         | 
| 287 | 
            +
                          return x;
         | 
| 288 | 
            +
                        }),
         | 
| 289 | 
            +
                        body: delta.deltaMessageReply.message.body || "",
         | 
| 290 | 
            +
                        isGroup: !!delta.deltaMessageReply.message.messageMetadata.threadKey.threadFbId,
         | 
| 291 | 
            +
                        mentions: mentions,
         | 
| 292 | 
            +
                        timestamp: delta.deltaMessageReply.message.messageMetadata.timestamp,
         | 
| 293 | 
            +
                      };
         | 
| 294 | 
            +
             | 
| 295 | 
            +
                      if (delta.deltaMessageReply.repliedToMessage) {
         | 
| 296 | 
            +
                        //Mention block - #2
         | 
| 297 | 
            +
                        mdata =
         | 
| 298 | 
            +
                          delta.deltaMessageReply.repliedToMessage === undefined ? [] :
         | 
| 299 | 
            +
                            delta.deltaMessageReply.repliedToMessage.data === undefined ? [] :
         | 
| 300 | 
            +
                              delta.deltaMessageReply.repliedToMessage.data.prng === undefined ? [] :
         | 
| 301 | 
            +
                                JSON.parse(delta.deltaMessageReply.repliedToMessage.data.prng);
         | 
| 302 | 
            +
                        m_id = mdata.map(u => u.i);
         | 
| 303 | 
            +
                        m_offset = mdata.map(u => u.o);
         | 
| 304 | 
            +
                        m_length = mdata.map(u => u.l);
         | 
| 305 | 
            +
             | 
| 306 | 
            +
                        var rmentions = {};
         | 
| 307 | 
            +
             | 
| 308 | 
            +
                        for (var i = 0; i < m_id.length; i++) {
         | 
| 309 | 
            +
                          rmentions[m_id[i]] = (delta.deltaMessageReply.repliedToMessage.body || "").substring(
         | 
| 310 | 
            +
                            m_offset[i],
         | 
| 311 | 
            +
                            m_offset[i] + m_length[i]
         | 
| 312 | 
            +
                          );
         | 
| 313 | 
            +
                        }
         | 
| 314 | 
            +
                        //Mention block - 2#
         | 
| 315 | 
            +
                        callbackToReturn.messageReply = {
         | 
| 316 | 
            +
                          threadID: (delta.deltaMessageReply.repliedToMessage.messageMetadata.threadKey.threadFbId ?
         | 
| 317 | 
            +
                            delta.deltaMessageReply.repliedToMessage.messageMetadata.threadKey.threadFbId : delta.deltaMessageReply.repliedToMessage.messageMetadata.threadKey
         | 
| 318 | 
            +
                              .otherUserFbId).toString(),
         | 
| 319 | 
            +
                          messageID: delta.deltaMessageReply.repliedToMessage.messageMetadata.messageId,
         | 
| 320 | 
            +
                          senderID: delta.deltaMessageReply.repliedToMessage.messageMetadata.actorFbId.toString(),
         | 
| 321 | 
            +
                          attachments: delta.deltaMessageReply.repliedToMessage.attachments.map(function (att) {
         | 
| 322 | 
            +
                            var mercury = JSON.parse(att.mercuryJSON);
         | 
| 323 | 
            +
                            Object.assign(att, mercury);
         | 
| 324 | 
            +
                            return att;
         | 
| 325 | 
            +
                          }).map(att => {
         | 
| 326 | 
            +
                            var x;
         | 
| 327 | 
            +
                            try {
         | 
| 328 | 
            +
                              x = utils._formatAttachment(att);
         | 
| 329 | 
            +
                            } catch (ex) {
         | 
| 330 | 
            +
                              x = att;
         | 
| 331 | 
            +
                              x.error = ex;
         | 
| 332 | 
            +
                              x.type = "unknown";
         | 
| 333 | 
            +
                            }
         | 
| 334 | 
            +
                            return x;
         | 
| 335 | 
            +
                          }),
         | 
| 336 | 
            +
                          body: delta.deltaMessageReply.repliedToMessage.body || "",
         | 
| 337 | 
            +
                          isGroup: !!delta.deltaMessageReply.repliedToMessage.messageMetadata.threadKey.threadFbId,
         | 
| 338 | 
            +
                          mentions: rmentions,
         | 
| 339 | 
            +
                          timestamp: delta.deltaMessageReply.repliedToMessage.messageMetadata.timestamp,
         | 
| 340 | 
            +
                        };
         | 
| 341 | 
            +
                      }
         | 
| 342 | 
            +
             | 
| 343 | 
            +
                      if (ctx.globalOptions.autoMarkDelivery) {
         | 
| 344 | 
            +
                        markDelivery(ctx, api, callbackToReturn.threadID, callbackToReturn.messageID);
         | 
| 345 | 
            +
                      }
         | 
| 346 | 
            +
             | 
| 347 | 
            +
                      return !ctx.globalOptions.selfListen &&
         | 
| 348 | 
            +
                        callbackToReturn.senderID === ctx.userID ?
         | 
| 349 | 
            +
                        undefined :
         | 
| 350 | 
            +
                        (function () { globalCallback(null, callbackToReturn); })();
         | 
| 351 | 
            +
                    }
         | 
| 352 | 
            +
                  }
         | 
| 353 | 
            +
                  return;
         | 
| 354 | 
            +
                }
         | 
| 355 | 
            +
              }
         | 
| 356 | 
            +
             | 
| 357 | 
            +
              if (v.delta.class !== "NewMessage" &&
         | 
| 358 | 
            +
                !ctx.globalOptions.listenEvents
         | 
| 359 | 
            +
              )
         | 
| 360 | 
            +
                return;
         | 
| 361 | 
            +
             | 
| 362 | 
            +
              switch (v.delta.class) {
         | 
| 363 | 
            +
                case "ReadReceipt":
         | 
| 364 | 
            +
                  var fmtMsg;
         | 
| 365 | 
            +
                  try {
         | 
| 366 | 
            +
                    fmtMsg = utils.formatDeltaReadReceipt(v.delta);
         | 
| 367 | 
            +
                  } catch (err) {
         | 
| 368 | 
            +
                    return globalCallback({
         | 
| 369 | 
            +
                      error: "Problem parsing message object. Please open an issue at https://github.com/Schmavery/facebook-chat-api/issues.",
         | 
| 370 | 
            +
                      detail: err,
         | 
| 371 | 
            +
                      res: v.delta,
         | 
| 372 | 
            +
                      type: "parse_error"
         | 
| 373 | 
            +
                    });
         | 
| 374 | 
            +
                  }
         | 
| 375 | 
            +
                  return (function () { globalCallback(null, fmtMsg); })();
         | 
| 376 | 
            +
                case "AdminTextMessage":
         | 
| 377 | 
            +
                  switch (v.delta.type) {
         | 
| 378 | 
            +
                    case "change_thread_theme":
         | 
| 379 | 
            +
                    case "change_thread_nickname":
         | 
| 380 | 
            +
                    case "change_thread_icon":
         | 
| 381 | 
            +
                      break;
         | 
| 382 | 
            +
                    case "group_poll":
         | 
| 383 | 
            +
                      var fmtMsg;
         | 
| 384 | 
            +
                      try {
         | 
| 385 | 
            +
                        fmtMsg = utils.formatDeltaEvent(v.delta);
         | 
| 386 | 
            +
                      } catch (err) {
         | 
| 387 | 
            +
                        return globalCallback({
         | 
| 388 | 
            +
                          error: "Problem parsing message object. Please open an issue at https://github.com/Schmavery/facebook-chat-api/issues.",
         | 
| 389 | 
            +
                          detail: err,
         | 
| 390 | 
            +
                          res: v.delta,
         | 
| 391 | 
            +
                          type: "parse_error"
         | 
| 392 | 
            +
                        });
         | 
| 393 | 
            +
                      }
         | 
| 394 | 
            +
                      return (function () { globalCallback(null, fmtMsg); })();
         | 
| 395 | 
            +
                    default:
         | 
| 396 | 
            +
                      return;
         | 
| 397 | 
            +
                  }
         | 
| 398 | 
            +
                  break;
         | 
| 399 | 
            +
                //For group images
         | 
| 400 | 
            +
                case "ForcedFetch":
         | 
| 401 | 
            +
                  if (!v.delta.threadKey) return;
         | 
| 402 | 
            +
                  var mid = v.delta.messageId;
         | 
| 403 | 
            +
                  var tid = v.delta.threadKey.threadFbId;
         | 
| 404 | 
            +
                  if (mid && tid) {
         | 
| 405 | 
            +
                    const form = {
         | 
| 406 | 
            +
                      "av": ctx.globalOptions.pageID,
         | 
| 407 | 
            +
                      "queries": JSON.stringify({
         | 
| 408 | 
            +
                        "o0": {
         | 
| 409 | 
            +
                          //This doc_id is valid as of ? (prob January 18, 2020)
         | 
| 410 | 
            +
                          "doc_id": "1768656253222505",
         | 
| 411 | 
            +
                          "query_params": {
         | 
| 412 | 
            +
                            "thread_and_message_id": {
         | 
| 413 | 
            +
                              "thread_id": tid.toString(),
         | 
| 414 | 
            +
                              "message_id": mid.toString(),
         | 
| 415 | 
            +
                            }
         | 
| 416 | 
            +
                          }
         | 
| 417 | 
            +
                        }
         | 
| 418 | 
            +
                      })
         | 
| 419 | 
            +
                    };
         | 
| 420 | 
            +
             | 
| 421 | 
            +
                    defaultFuncs
         | 
| 422 | 
            +
                      .post("https://www.facebook.com/api/graphqlbatch/", ctx.jar, form)
         | 
| 423 | 
            +
                      .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
         | 
| 424 | 
            +
                      .then((resData) => {
         | 
| 425 | 
            +
                        if (resData[resData.length - 1].error_results > 0) {
         | 
| 426 | 
            +
                          throw resData[0].o0.errors;
         | 
| 427 | 
            +
                        }
         | 
| 428 | 
            +
             | 
| 429 | 
            +
                        if (resData[resData.length - 1].successful_results === 0) {
         | 
| 430 | 
            +
                          throw { error: "forcedFetch: there was no successful_results", res: resData };
         | 
| 431 | 
            +
                        }
         | 
| 432 | 
            +
             | 
| 433 | 
            +
                        var fetchData = resData[0].o0.data.message;
         | 
| 434 | 
            +
                        if (fetchData && fetchData.__typename === "ThreadImageMessage") {
         | 
| 435 | 
            +
                          (!ctx.globalOptions.selfListen &&
         | 
| 436 | 
            +
                            fetchData.message_sender.id.toString() === ctx.userID) ||
         | 
| 437 | 
            +
                            !ctx.loggedIn ?
         | 
| 438 | 
            +
                            undefined :
         | 
| 439 | 
            +
                            (function () { globalCallback(null, {
         | 
| 440 | 
            +
                              type: "change_thread_image",
         | 
| 441 | 
            +
                              threadID: utils.formatID(tid.toString()),
         | 
| 442 | 
            +
                              snippet: fetchData.snippet,
         | 
| 443 | 
            +
                              timestamp: fetchData.timestamp_precise,
         | 
| 444 | 
            +
                              author: fetchData.message_sender.id,
         | 
| 445 | 
            +
                              image: {
         | 
| 446 | 
            +
                                attachmentID: fetchData.image_with_metadata && fetchData.image_with_metadata.legacy_attachment_id,
         | 
| 447 | 
            +
                                width: fetchData.image_with_metadata && fetchData.image_with_metadata.original_dimensions.x,
         | 
| 448 | 
            +
                                height: fetchData.image_with_metadata && fetchData.image_with_metadata.original_dimensions.y,
         | 
| 449 | 
            +
                                url: fetchData.image_with_metadata && fetchData.image_with_metadata.preview.uri
         | 
| 450 | 
            +
                              }
         | 
| 451 | 
            +
                            }); })();
         | 
| 452 | 
            +
                        }
         | 
| 453 | 
            +
                      })
         | 
| 454 | 
            +
                      .catch((err) => {
         | 
| 455 | 
            +
                        log.error("forcedFetch", err);
         | 
| 456 | 
            +
                      });
         | 
| 457 | 
            +
                  }
         | 
| 458 | 
            +
                  break;
         | 
| 459 | 
            +
                case "ThreadName":
         | 
| 460 | 
            +
                case "ParticipantsAddedToGroupThread":
         | 
| 461 | 
            +
                case "ParticipantLeftGroupThread":
         | 
| 462 | 
            +
                  var formattedEvent;
         | 
| 463 | 
            +
                  try {
         | 
| 464 | 
            +
                    formattedEvent = utils.formatDeltaEvent(v.delta);
         | 
| 465 | 
            +
                  } catch (err) {
         | 
| 466 | 
            +
                    return globalCallback({
         | 
| 467 | 
            +
                      error: "Problem parsing message object. Please open an issue at https://github.com/Schmavery/facebook-chat-api/issues.",
         | 
| 468 | 
            +
                      detail: err,
         | 
| 469 | 
            +
                      res: v.delta,
         | 
| 470 | 
            +
                      type: "parse_error"
         | 
| 471 | 
            +
                    });
         | 
| 472 | 
            +
                  }
         | 
| 473 | 
            +
                  return (!ctx.globalOptions.selfListen &&
         | 
| 474 | 
            +
                    formattedEvent.author.toString() === ctx.userID) ||
         | 
| 475 | 
            +
                    !ctx.loggedIn ?
         | 
| 476 | 
            +
                    undefined :
         | 
| 477 | 
            +
                    (function () { globalCallback(null, formattedEvent); })();
         | 
| 478 | 
            +
              }
         | 
| 479 | 
            +
            }
         | 
| 480 | 
            +
             | 
| 481 | 
            +
            function markDelivery(ctx, api, threadID, messageID) {
         | 
| 482 | 
            +
              if (threadID && messageID) {
         | 
| 483 | 
            +
                api.markAsDelivered(threadID, messageID, (err) => {
         | 
| 484 | 
            +
                  if (err) {
         | 
| 485 | 
            +
                    log.error(err);
         | 
| 486 | 
            +
                  } else {
         | 
| 487 | 
            +
                    if (ctx.globalOptions.autoMarkRead) {
         | 
| 488 | 
            +
                      api.markAsRead(threadID, (err) => {
         | 
| 489 | 
            +
                        if (err) {
         | 
| 490 | 
            +
                          log.error(err);
         | 
| 491 | 
            +
                        }
         | 
| 492 | 
            +
                      });
         | 
| 493 | 
            +
                    }
         | 
| 494 | 
            +
                  }
         | 
| 495 | 
            +
                });
         | 
| 496 | 
            +
              }
         | 
| 497 | 
            +
            }
         | 
| 498 | 
            +
             | 
| 499 | 
            +
            module.exports = function (defaultFuncs, api, ctx) {
         | 
| 500 | 
            +
              var globalCallback = identity;
         | 
| 501 | 
            +
              return function (callback) {
         | 
| 502 | 
            +
                globalCallback = callback;
         | 
| 503 | 
            +
             | 
| 504 | 
            +
                //Reset some stuff
         | 
| 505 | 
            +
                ctx.lastSeqId = 0;
         | 
| 506 | 
            +
                ctx.syncToken = undefined;
         | 
| 507 | 
            +
             | 
| 508 | 
            +
                //Same request as getThreadList
         | 
| 509 | 
            +
                const form = {
         | 
| 510 | 
            +
                  "av": ctx.globalOptions.pageID,
         | 
| 511 | 
            +
                  "queries": JSON.stringify({
         | 
| 512 | 
            +
                    "o0": {
         | 
| 513 | 
            +
                      "doc_id": "1349387578499440",
         | 
| 514 | 
            +
                      "query_params": {
         | 
| 515 | 
            +
                        "limit": 1,
         | 
| 516 | 
            +
                        "before": null,
         | 
| 517 | 
            +
                        "tags": ["INBOX"],
         | 
| 518 | 
            +
                        "includeDeliveryReceipts": false,
         | 
| 519 | 
            +
                        "includeSeqID": true
         | 
| 520 | 
            +
                      }
         | 
| 521 | 
            +
                    }
         | 
| 522 | 
            +
                  })
         | 
| 523 | 
            +
                };
         | 
| 524 | 
            +
             | 
| 525 | 
            +
                defaultFuncs
         | 
| 526 | 
            +
                  .post("https://www.facebook.com/api/graphqlbatch/", ctx.jar, form)
         | 
| 527 | 
            +
                  .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
         | 
| 528 | 
            +
                  .then((resData) => {
         | 
| 529 | 
            +
                    if (resData && resData.length > 0 && resData[resData.length - 1].error_results > 0) {
         | 
| 530 | 
            +
                      throw resData[0].o0.errors;
         | 
| 531 | 
            +
                    }
         | 
| 532 | 
            +
             | 
| 533 | 
            +
                    if (resData[resData.length - 1].successful_results === 0) {
         | 
| 534 | 
            +
                      throw { error: "getSeqId: there was no successful_results", res: resData };
         | 
| 535 | 
            +
                    }
         | 
| 536 | 
            +
             | 
| 537 | 
            +
                    if (resData[0].o0.data.viewer.message_threads.sync_sequence_id) {
         | 
| 538 | 
            +
                      ctx.lastSeqId = resData[0].o0.data.viewer.message_threads.sync_sequence_id;
         | 
| 539 | 
            +
                      listenMqtt(defaultFuncs, api, ctx, globalCallback);
         | 
| 540 | 
            +
                    }
         | 
| 541 | 
            +
             | 
| 542 | 
            +
                  })
         | 
| 543 | 
            +
                  .catch((err) => {
         | 
| 544 | 
            +
                    log.error("getSeqId", err);
         | 
| 545 | 
            +
                    return callback(err);
         | 
| 546 | 
            +
                  });
         | 
| 547 | 
            +
             | 
| 548 | 
            +
                var stopListening = function () {
         | 
| 549 | 
            +
                  globalCallback = identity;
         | 
| 550 | 
            +
             | 
| 551 | 
            +
                  if(ctx.mqttClient)
         | 
| 552 | 
            +
                  {
         | 
| 553 | 
            +
                    ctx.mqttClient.end();
         | 
| 554 | 
            +
                    ctx.mqttClient = undefined;
         | 
| 555 | 
            +
                  }
         | 
| 556 | 
            +
                };
         | 
| 557 | 
            +
             | 
| 558 | 
            +
                return stopListening;
         | 
| 559 | 
            +
              };
         | 
| 560 | 
            +
            };
         | 
    
        package/alice/logout.js
    ADDED
    
    | @@ -0,0 +1,59 @@ | |
| 1 | 
            +
            "use strict";
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            var utils = require("../utils");
         | 
| 4 | 
            +
            var log = require("npmlog");
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            module.exports = function(defaultFuncs, bot, ctx) {
         | 
| 7 | 
            +
              return function logout(callback) {
         | 
| 8 | 
            +
                callback = callback || function() {};
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                var form = {
         | 
| 11 | 
            +
                  pmid: "0"
         | 
| 12 | 
            +
                };
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                defaultFuncs
         | 
| 15 | 
            +
                  .post(
         | 
| 16 | 
            +
                    "https://www.facebook.com/bluebar/modern_settings_menu/?help_type=364455653583099&show_contextual_help=1",
         | 
| 17 | 
            +
                    ctx.jar,
         | 
| 18 | 
            +
                    form
         | 
| 19 | 
            +
                  )
         | 
| 20 | 
            +
                  .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
         | 
| 21 | 
            +
                  .then(function(resData) {
         | 
| 22 | 
            +
                    var elem = resData.jsmods.instances[0][2][0].filter(function(v) {
         | 
| 23 | 
            +
                      return v.value === "logout";
         | 
| 24 | 
            +
                    })[0];
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                    var html = resData.jsmods.markup.filter(function(v) {
         | 
| 27 | 
            +
                      return v[0] === elem.markup.__m;
         | 
| 28 | 
            +
                    })[0][1].__html;
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                    var form = {
         | 
| 31 | 
            +
                      fb_dtsg: utils.getFrom(html, '"fb_dtsg" value="', '"'),
         | 
| 32 | 
            +
                      ref: utils.getFrom(html, '"ref" value="', '"'),
         | 
| 33 | 
            +
                      h: utils.getFrom(html, '"h" value="', '"')
         | 
| 34 | 
            +
                    };
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                    return defaultFuncs
         | 
| 37 | 
            +
                      .post("https://www.facebook.com/logout.php", ctx.jar, form)
         | 
| 38 | 
            +
                      .then(utils.saveCookies(ctx.jar));
         | 
| 39 | 
            +
                  })
         | 
| 40 | 
            +
                  .then(function(res) {
         | 
| 41 | 
            +
                    if (!res.headers) {
         | 
| 42 | 
            +
                      throw { error: "An error occurred when logging out." };
         | 
| 43 | 
            +
                    }
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                    return defaultFuncs
         | 
| 46 | 
            +
                      .get(res.headers.location, ctx.jar)
         | 
| 47 | 
            +
                      .then(utils.saveCookies(ctx.jar));
         | 
| 48 | 
            +
                  })
         | 
| 49 | 
            +
                  .then(function() {
         | 
| 50 | 
            +
                    ctx.loggedIn = false;
         | 
| 51 | 
            +
                    log.info("logout", "Logged out successfully.");
         | 
| 52 | 
            +
                    callback();
         | 
| 53 | 
            +
                  })
         | 
| 54 | 
            +
                  .catch(function(err) {
         | 
| 55 | 
            +
                    log.error("logout", err);
         | 
| 56 | 
            +
                    return callback(err);
         | 
| 57 | 
            +
                  });
         | 
| 58 | 
            +
              };
         | 
| 59 | 
            +
            };
         | 
| @@ -0,0 +1,51 @@ | |
| 1 | 
            +
            "use strict";
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            var utils = require("../utils");
         | 
| 4 | 
            +
            var log = require("npmlog");
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            module.exports = function(defaultFuncs, bot, ctx) {
         | 
| 7 | 
            +
              return function handleMessageRequest(threadID, accept, callback) {
         | 
| 8 | 
            +
                if (utils.getType(accept) !== "Boolean") {
         | 
| 9 | 
            +
                  throw {
         | 
| 10 | 
            +
                    error: "Please pass a boolean as a second argument."
         | 
| 11 | 
            +
                  };
         | 
| 12 | 
            +
                }
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                if (!callback) {
         | 
| 15 | 
            +
                  callback = function() {};
         | 
| 16 | 
            +
                }
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                var form = {
         | 
| 19 | 
            +
                  client: "mercury"
         | 
| 20 | 
            +
                };
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                if (utils.getType(threadID) !== "Array") {
         | 
| 23 | 
            +
                  threadID = [threadID];
         | 
| 24 | 
            +
                }
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                var messageBox = accept ? "inbox" : "other";
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                for (var i = 0; i < threadID.length; i++) {
         | 
| 29 | 
            +
                  form[messageBox + "[" + i + "]"] = threadID[i];
         | 
| 30 | 
            +
                }
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                defaultFuncs
         | 
| 33 | 
            +
                  .post(
         | 
| 34 | 
            +
                    "https://www.facebook.com/ajax/mercury/move_thread.php",
         | 
| 35 | 
            +
                    ctx.jar,
         | 
| 36 | 
            +
                    form
         | 
| 37 | 
            +
                  )
         | 
| 38 | 
            +
                  .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
         | 
| 39 | 
            +
                  .then(function(resData) {
         | 
| 40 | 
            +
                    if (resData.error) {
         | 
| 41 | 
            +
                      throw resData;
         | 
| 42 | 
            +
                    }
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                    return callback();
         | 
| 45 | 
            +
                  })
         | 
| 46 | 
            +
                  .catch(function(err) {
         | 
| 47 | 
            +
                    log.error("handleMessageRequest", err);
         | 
| 48 | 
            +
                    return callback(err);
         | 
| 49 | 
            +
                  });
         | 
| 50 | 
            +
              };
         | 
| 51 | 
            +
            };
         | 
    
        package/alice/mute.js
    ADDED
    
    | @@ -0,0 +1,38 @@ | |
| 1 | 
            +
            "use strict";
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            var utils = require("../utils");
         | 
| 4 | 
            +
            var log = require("npmlog");
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            module.exports = function(defaultFuncs, bot, ctx) {
         | 
| 7 | 
            +
              // muteSecond: -1=permanent mute, 0=unmute, 60=one minute, 3600=one hour, etc.
         | 
| 8 | 
            +
              return function muteThread(threadID, muteSeconds, callback) {
         | 
| 9 | 
            +
                if (!callback) {
         | 
| 10 | 
            +
                  callback = function() {};
         | 
| 11 | 
            +
                }
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                var form = {
         | 
| 14 | 
            +
                  thread_fbid: threadID,
         | 
| 15 | 
            +
                  mute_settings: muteSeconds
         | 
| 16 | 
            +
                };
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                defaultFuncs
         | 
| 19 | 
            +
                  .post(
         | 
| 20 | 
            +
                    "https://www.facebook.com/ajax/mercury/change_mute_thread.php",
         | 
| 21 | 
            +
                    ctx.jar,
         | 
| 22 | 
            +
                    form
         | 
| 23 | 
            +
                  )
         | 
| 24 | 
            +
                  .then(utils.saveCookies(ctx.jar))
         | 
| 25 | 
            +
                  .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
         | 
| 26 | 
            +
                  .then(function(resData) {
         | 
| 27 | 
            +
                    if (resData.error) {
         | 
| 28 | 
            +
                      throw resData;
         | 
| 29 | 
            +
                    }
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                    return callback();
         | 
| 32 | 
            +
                  })
         | 
| 33 | 
            +
                  .catch(function(err) {
         | 
| 34 | 
            +
                    log.error("muteThread", err);
         | 
| 35 | 
            +
                    return callback(err);
         | 
| 36 | 
            +
                  });
         | 
| 37 | 
            +
              };
         | 
| 38 | 
            +
            };
         |