简体   繁体   English

处理Gmail加载项中持久的特定于用户的值

[英]Handling persistent user-specific values in Gmail Add-ons

I have created simple Gmail addon, now I'm struggling with below strategies. 我创建了简单的Gmail插件,现在我正在努力应对以下策略。

After installing the addon, when first inbox is opened, we ask basic info input from users, when he opens second mail we won't ask basic info details again. 安装插件后,当第一个收件箱打开时,我们会询问用户输入的基本信息,当他打开第二封邮件时,我们不会再次询问基本信息详情。

How I can handle this? 我怎么能处理这个?

I have tried property service but no luck. 我尝试过物业服务,但没有运气。

Update 更新

var MAX_THREADS = 5;

/**
 * Returns the array of cards that should be rendered for the current
 * e-mail thread. The name of this function is specified in the
 * manifest 'onTriggerFunction' field, indicating that this function
 * runs every time the add-on is started.
 *
 * @param {Object} e data provided by the Gmail UI.
 * @returns {Card[]}
 */
function buildAddOn(e) {
  // Activate temporary Gmail add-on scopes.
  //Logger.log('E', Session.getActiveUser());
  var accessToken = e.messageMetadata.accessToken;
  GmailApp.setCurrentMessageAccessToken(accessToken);
  var userProperties = PropertiesService.getUserProperties();
  var Token = userProperties.getProperty('Token');
  Logger.log('Token value:',typeof Token);
  if(Token != null ){
    var messageId = e.messageMetadata.messageId;
    var senderData = extractSenderData(messageId);
    var cards = [];

    // Build a card for each recent thread from this email's sender.
    if (senderData.recents.length > 0) {
      senderData.recents.forEach(function(threadData) {
        cards.push(buildRecentThreadCard(senderData.email, threadData));
      });
    } else {
      // Present a blank card if there are no recent threads from
      // this sender.
      cards.push(CardService.newCardBuilder()
        .setHeader(CardService.newCardHeader()
        .setTitle('No recent threads from this sender')).build());
    }
    return cards;
  } 
  else{
    var cards = []
    var login_card = build_login_card()
    cards.push(login_card);
    return cards;
  }
}

/**
 *  This function builds a set of data about this sender's presence in your
 *  inbox.
 *
 *  @param {String} messageId The message ID of the open message.
 *  @return {Object} a collection of sender information to display in cards.
 */
function extractSenderData(messageId) {
  // Use the Gmail service to access information about this message.
  var mail = GmailApp.getMessageById(messageId);
  var threadId = mail.getThread().getId();
  var senderEmail = extractEmailAddress(mail.getFrom());

  var recentThreads = GmailApp.search('from:' + senderEmail);
  var recents = [];

  // Retrieve information about up to 5 recent threads from the same sender.
  recentThreads.slice(0,MAX_THREADS).forEach(function(thread) {
    if (thread.getId() != threadId && ! thread.isInChats()) {
      recents.push({
        'subject': thread.getFirstMessageSubject(),
        'count': thread.getMessageCount(),
        'link': 'https://mail.google.com/mail/u/0/#inbox/' + thread.getId(),
        'lastDate': thread.getLastMessageDate().toDateString()
      });
    }
  });

  var senderData = {
    "email": senderEmail,
    'recents': recents
  };

  return senderData;
}

/**
 *  Given the result of GmailMessage.getFrom(), extract only the email address.
 *  getFrom() can return just the email address or a string in the form
 *  "Name <myemail@domain>".
 *
 *  @param {String} sender The results returned from getFrom().
 *  @return {String} Only the email address.
 */
function extractEmailAddress(sender) {
  var regex = /\<([^\@]+\@[^\>]+)\>/;
  var email = sender;  // Default to using the whole string.
  var match = regex.exec(sender);
  if (match) {
    email = match[1];
  }
  return email;
}

/**
 *  Builds a card to display information about a recent thread from this sender.
 *
 *  @param {String} senderEmail The sender email.
 *  @param {Object} threadData Infomation about the thread to display.
 *  @return {Card} a card that displays thread information.
 */
function buildRecentThreadCard(senderEmail, threadData) {
  var card = CardService.newCardBuilder();
  card.setHeader(CardService.newCardHeader().setTitle(threadData.subject));
  var section = CardService.newCardSection()
    .setHeader("<font color=\"#1257e0\">Recent thread</font>");
  section.addWidget(CardService.newTextParagraph().setText(threadData.subject));
  section.addWidget(CardService.newKeyValue()
    .setTopLabel('Sender')
    .setContent(senderEmail));
  section.addWidget(CardService.newKeyValue()
    .setTopLabel('Number of messages')
    .setContent(threadData.count.toString()));
  section.addWidget(CardService.newKeyValue()
    .setTopLabel('Last updated')
    .setContent(threadData.lastDate.toString()));

  var threadLink = CardService.newOpenLink()
    .setUrl(threadData.link)
    .setOpenAs(CardService.OpenAs.FULL_SIZE);
  var button = CardService.newTextButton()
    .setText('Open Thread')
    .setOpenLink(threadLink);
  section.addWidget(CardService.newButtonSet().addButton(button));

  card.addSection(section);
  return card.build();
}
function build_login_card(){
  var card = CardService.newCardBuilder();
  card.setHeader(CardService.newCardHeader().setTitle("Login Here"));
  var userProperties = PropertiesService.getUserProperties();
  var Token = userProperties.setProperty('Token',"Token");
  return card.build()
}

Robert , have you tried caching the user input ? 罗伯特,你试过缓存用户输入吗?

I do caching in the event handler. 我在事件处理程序中进行缓存。

 function onDomainChange(e){ var cache = CacheService.getScriptCache(); Logger.log(e.formInput); cache.put('domain',e.formInput.domain); Logger.log(cache.get('domain')); } 

Refer cache docs 请参阅缓存文档

According to comments, the primary issue here is that uninstalling the add-on does not remove bits from the UserProperties datastore, and thus if the add-on is re-installed, the add-on configuration steps cannot be performed again. 根据评论,这里的主要问题是卸载加载项不会从UserProperties数据存储中删除位,因此如果重新安装加载项,则无法再次执行加载项配置步骤。

Generic Add-on Solution 通用附加解决方案

If this were not a Gmail add-on, this could be simply solved with a time-based trigger, since per documentation: 如果这不是Gmail附加组件,那么可以通过基于时间的触发器简单地解决这个问题,因为每个文档:

Add-on triggers will stop firing in any of the following situations: 附加触发器将在以下任何一种情况下停止触发:
- If the add-on is uninstalled by the user - 如果用户卸载了加载项
- If the add-on is disabled in a document (if it is re-enabled, the trigger will become operational again) - 如果文档中禁用了加载项(如果重新启用,则触发器将再次运行)
- If the developer unpublishes the add-on or submits a broken version to the add-on store - 如果开发人员取消发布加载项或将加载版本提交到加载项存储

Ie, you would simply write the day's date to a key (eg LAST_SEEN ) in PropertiesService#UserProperties every day, and then check both TOKEN and LAST_SEEN when deciding which card to display. 即,您只需每天在PropertiesService#UserProperties日期的日期写入密钥(例如LAST_SEEN ),然后在决定显示哪张卡时检查TOKEN和LAST_SEEN。


Gmail Add-on Solution: Gmail加载项解决方案:

Per Gmail add-on documentation, you cannot create or use Apps Script simple / installable triggers in a Gmail add-on. 根据Gmail附加组件文档,您无法在Gmail插件中创建或使用Apps Script简单/可安装触发器。 So, the easiest implementation of the solution is not available. 因此,该解决方案的最简单实施是不可用的。 However, we can still use this approach, by moving the region in which that LAST_SEEN key is updated. 但是,我们仍然可以通过移动更新LAST_SEEN键的区域来使用此方法。

Right now (2018 March), the only available trigger for a Gmail add-on is the contextual trigger unconditional : 现在(3月2018年),Gmail加载项的唯一可用触发器是unconditional上下文触发器:

Currently, the only contextual trigger type available is unconditional , which triggers for all emails regardless of content. 目前,唯一可用的上下文触发类型是无条件的 ,无论内容如何,​​都会触发所有电子邮件。

So, if you bind to this contextual trigger, while your add-on is installed it will run a function whenever the user opens an email. 因此,如果绑定到此上下文触发器,则在安装加载项时,只要用户打开电子邮件,它就会运行一个函数。 The solution is then for your contextually triggered function to include the snippet 然后,解决方案是针对您的上下文触发函数包含代码段

PropertiesService.getUserProperties().setProperty("LAST_SEEN", String(new Date().getTime()));

If you have other stuff to do in your contextually triggered function, that code will be uninfluenced by this addition. 如果您在上下文触发的函数中有其他事情要做,那么该代码将不受此添加的影响。 If you don't have a contextually triggered function, then you'd want to return [] (an empty card stack) in order to avoid showing any UI. 如果您没有上下文触发的函数,那么您需要return [] (空卡堆栈)以避免显示任何UI。

To use this new property, in your buildAddon(e) method you want to query for this value in addition to the TOKEN property you are using: 要使用此新属性,除了要使用的TOKEN属性之外,还要在buildAddon(e)方法中查询此值:

var userProps = PropertiesService.getUserProperties().getAll();
var Token = userProps["Token"];
var lastSeen = userProps["LAST_SEEN"] || 0; // If found, will be milliseconds since epoch.
var absence = new Date().getTime() - lastSeen; // Time in ms since last use of add-on.
if (Token == null || absence > /* some duration you choose */ ) {
  // New install, or user has started using app again.
  return [build_login_card()];
} else {
  // User is still using add-on, so do normal stuff.
}

  • This is obviously not a perfect solution (ie an uninstall contextual trigger would be much better), but can help detect lack-of-use situations 这显然不是一个完美的解决方案(即uninstall上下文触发器会好得多),但可以帮助检测缺乏使用的情况
  • There are rate limits on how often you can write to PropertiesService. 您可以写入PropertiesService的频率有限制。 If you have speedy/"power" users, they might trip quotas. 如果你有快速/“动力”用户,他们可能会配额。
    • Could combine CacheService and PropertiesService to handle frequent reads in a "short" session (of up to 6hr since last storage into cache). 可以将CacheService和PropertiesService结合起来处理“短”会话中的频繁读取(自上次存储到缓存以来最多6小时)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM