处理Gmail加载项中的持久性用户特定值 [英] Handling persistent user-specific values in Gmail Add-ons

查看:137
本文介绍了处理Gmail加载项中的持久性用户特定值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



安装插件后,打开第一个收件箱后,我们要求输入基本信息从用户那里,当他打开第二封邮件时,我们不会再询问基本信息的详细信息。



我可以如何处理?

我尝试了属性服务,但没有运气。



更新

  var MAX_THREADS = 5; 

/ **
*返回应为当前
*电子邮件线程呈现的卡片数组。该函数的名称在
*清单'onTriggerFunction'字段中指定,表明该函数
*在每次启动附件时运行。
*
* @param {对象}由Gmail用户界面提供的数据。
* @returns {Card []}
* /
函数buildAddOn(e){
//激活临时的Gmail附加范围。
//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 = [];

//为此电子邮件发件人的每个最近线索构建一张卡片。
if(senderData.recents.length> 0){
senderData.recents.forEach(function(threadData){
cards.push(buildRecentThreadCard(senderData.email,threadData));
});
} else {
//如果没有来自
的最近线程//此发件人,则显示空白卡片。
cards.push(CardService.newCardBuilder())
.setHeader(CardService.newCardHeader()
.setTitle('没有最近的线程从这个发送者'))。build());
}
退回卡;
}
else {
var cards = []
var login_card = build_login_card()
cards.push(login_card);
退回卡;
}
}

/ **
*此函数建立一组有关此发件人在
*收件箱中的数据。
*
* @param {String} messageId打开消息的消息ID。
* @return {Object}将一组发件人信息显示在卡片中。
* /
函数extractSenderData(messageId){
//使用Gmail服务访问关于此消息的信息。
var mail = GmailApp.getMessageById(messageId);
var threadId = mail.getThread()。getId();
var senderEmail = extractEmailAddress(mail.getFrom());

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

//从同一个发件人检索最多5个最近线程的信息。
latestThreads.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;
}

/ **
*根据GmailMessage.getFrom()的结果,只提取电子邮件地址。
* getFrom()只能返回电子邮件地址或形式为
*Name< myemail @ domain>的字符串。
*
* @param {String} sender从getFrom()返回的结果。
* @return {String}只有电子邮件地址。
* /
function extractEmailAddress(sender){
var regex = / \<([^ \ @] + \ @ [^ \>] +)\ > /;
var email = sender; //默认使用整个字符串。
var match = regex.exec(sender);
if(match){
email = match [1];
}
返回电子邮件;
}

/ **
*构建一张卡片以显示来自此发件人的最近线索的信息。
*
* @param {String} senderEmail发件人电子邮件。
* @param {Object} threadData关于要显示的线程的信息。
* @return {Card}显示线程信息的卡片。
* /
函数buildRecentThreadCard(senderEmail,threadData){
var card = CardService.newCardBuilder();
card.setHeader(CardService.newCardHeader()。setTitle(threadData.subject));
var section = CardService.newCardSection()
.setHeader(< font color = \#1257e0 \>最近线程< / 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();
}
函数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()
}


解决方案

通用附加解决方案



如果这不是Gmail附加组件,使用基于时间的触发器解决,因为每个文档:
$ b


在以下任何情况下,加载项触发器都会停止触发:

- 如果加载项是由用户卸载

- 如果在文档中禁用附加组件(如果它被重新启用,触发器将再次运行)

- 如果开发者未取消发布附加或提交破损的版本附加存储


即,您只需将当天的日期写入某个键(例如, LAST_SEEN )放在 PropertiesService#UserProperties 中,然后在决定要显示哪个卡时检查TOKEN和LAST_SEEN。 / p>




Gmail附加解决方案:



根据Gmail附加文档,您无法创建或使用Apps Script简单/可安装触发器在Gmail附加组件中。因此,解决方案的最简单的实现不可用。但是,我们仍然可以使用这种方法,即移动更新 LAST_SEEN 键的区域。



正确现在(2018年3月),Gmail插件的唯一可用触发器是上下文触发器无条件


目前,只有可用的上下文触发器类型是无条件的,它触发所有电子邮件而不管内容。 你绑定到这个上下文触发器,当你的加载项被安装时,只要用户打开一封邮件,它就会运行一个函数。然后,解决方案为您的上下文触发函数包含片段

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

如果您的上下文触发函数中有其他内容需要执行,则该代码将不受此添加的影响。如果你没有上下文触发的函数,那么你需要 return [] (一个空的卡片堆栈)以避免显示任何UI。



要使用这个新属性,除了TOKEN之外,还需要在 buildAddon(e)方法中查询该值您正在使用的财产:

  var userProps = PropertiesService.getUserProperties()。getAll(); 
var Token = userProps [Token];
var lastSeen = userProps [LAST_SEEN] || 0; //如果找到,自从时代起将是毫秒。
var absence = new Date()。getTime() - lastSeen; //自上次使用加载项以来的毫秒数。
if(Token == null || absence> / *您选择的持续时间* /){
//新安装,或者用户已经开始再次使用应用程序。
return [build_login_card()];
} else {
//用户仍然在使用附加组件,所以做普通的东西。

$ / code>







  • 这显然不是一个完美的解决方案(即 uninstall 上下文触发器会好得多),但可以帮助检测缺少使用情况。

  • 您可以写入PropertiesService的频率有限制。如果你有快速/强大的用户,他们可能会跳闸配额。


    • 可以结合CacheService和PropertiesService来处理短会话中的频繁读取(自从上次存储到缓存后长达6小时)。



I have created simple Gmail addon, now I'm struggling with below strategies.

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()
}

解决方案

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.

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:

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

I.e., you would simply write the day's date to a key (e.g. LAST_SEEN) in PropertiesService#UserProperties every day, and then check both TOKEN and LAST_SEEN when deciding which card to display.


Gmail Add-on Solution:

Per Gmail add-on documentation, you cannot create or use Apps Script simple / installable triggers in a Gmail add-on. 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.

Right now (2018 March), the only available trigger for a Gmail add-on is the contextual trigger 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.

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:

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 (i.e. an uninstall contextual trigger would be much better), but can help detect lack-of-use situations
  • There are rate limits on how often you can write to 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).

这篇关于处理Gmail加载项中的持久性用户特定值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆