В
gmail можно делать закладки на определенные письма/цепочки писем.
Обычно ссылка имеет вид
https://mail.google.com/mail/#all/124e3d1d452af489
,где "
124e3d1d452af489" - уникальный id писма/цепочки писем.
Например, ваши заказы хранятся в таблице и было-бы здорово сделать ссылку на gmail цепочку по каждой заявке.
Пока что я написал только часть скрипта. У меня база хранится в
google spreadsheets, ну а у вас это может быть mySql или что-то еще.
Как вытащить данные из таблиц гугл вы можете посмотреть
тут (на английском, но всё очень подробно и понятно).
Да и еще, пока я ковырялся с php, в google apps scripts появилась возможность работать с gmail, так что скорее всего
Я не буду дописывать этот скрипт (ну там как всегда чего-то не работает, чего-то не хватает - нафиг -
буду продолжать этот скрипт). Но если что-то нужно - спрашивайте.
Итак Я использую
ZEND framework, а именно класс
Zend/Mail/Protocol/Imap.php (версия, с которой Я работал) и дополняющий класс, который написал сам -
shkurIMAPclassExtendsZendProtocolImap.php
И собственно
сам скрипт: (есть версия посвежее - если надо спрашивайте - выложу)
<?php
echo 'Не забудьте включить IMAP в настройках почты!';
$login = "user@exmple.com"; //обязательно вида user@domain.com
$pass = "pass";
//require_once "Zend/Mail/Storage/Imap.php"; //кагбэ не нужен
require_once "Zend/Mail/Protocol/Imap.php";
//require_once "Zend/Registry.php"; //кагбэ не нужен
require_once "shkurIMAPclassExtendsZendProtocolImap.php"; // достаточно инициализировать только дочерний класс
//$protocol = new Zend_Mail_Protocol_Imap('imap.gmail.com', 993, true);
$protocol = new shkur_IMAP_class_Extends_Zend_Mail_Protocol_Imap('imap.gmail.com', 993, true);
$protocol->login($login, $pass);
// попробую написать чтобы скрипт сам выбирал папку All mail независимо от локлизации. utf-7 imap кривая кодировка
$xlist = $protocol->xlist();
//print_r($xlist);
$found = array_keys($protocol->getParentStack('\AllMail', $xlist));
$allMail = $found[0];
//$storage = new Zend_Mail_Storage_Imap($protocol); //кагбэ не нужен
//$protocol->select("INBOX"); нам нужна вся почта а это чуть ниже абракадабра
$protocol->select($allMail); // select надо вызывать после $storage = new Zend_Mail_Storage_Imap($protocol); иначе поиск смотрит в INBOX; метод echo $storage->getCurrentFolder(); в любом случае будет выводить INBOX - убиваем перфекциониста - идём по простому пути - работает и х_й с ним.
//echo $storage->getCurrentFolder(); //ret INBOX в любом случае - неправильно что-то ну хрен с ним
//echo '<br>'; echo "<pre>"; print_r($protocol->listMailbox()); echo "</pre>"; echo '<br>'; массив ящики
//echo $protocol->fetch('X-GM-MSGID', $storage->getUniqueId(1)); // ret 1327644189674155015 то что надо! ура!!!
$findthis = "test"; // qwertyuiop живалов&test
echo 'ищщем → <b>'.$findthis.'</b><br>';
//$search = $protocol->search(array("charset utf-8 text", $findthis)); // вот так работает - это поиск через imap протокол низзя пробелы - можно & или |
$search = $protocol->search(array("charset utf-8 X-GM-RAW", $findthis)); //более жадный поиск, например по запорсу живалов|test (это разные цепочки и разные письма) imap ни чего не найдёт, а гугло поиск даст и те и другие письма!
echo "\nкол-во найденных писем по запросу $findthis = ".count($search)."<br>";
if (count($search) < 1 ){ exit('ни чего не найдено в почте. exit');} // или break, exit может прервать выполнение всего скрипта.
//echo "\nsearch= "; print_r($search);
//echo $decID = $protocol->fetch('X-GM-MSGID', $storage->getUniqueId($search[0])-3); //почему-то ставит 50 вместо 47
//echo $decID = $protocol->fetch('X-GM-MSGID', $search[0]); //вот так работает и без всяких getUniqueId
$decID = $protocol->fetch('X-GM-THRID', $search);
$decID = array_unique($decID); // убрать повторяющиеся значения - несколько писем в одной цепочке.
//if ($decID) {} else {echo 'переменной нет';}
//echo "\n$decID= "; print_r($decID);
if ($decID){ // проверка на существование массива/переменной, т.к. если ни чего нет, то будет ошибка foreach
foreach ($decID as $k => $v) {
$hexID[]=$protocol->dec2hex($v);
}
}
/** подготовим почву для ссылки, которая должна быть вида https://mail.google.com/mail/#all/13262ab3255034e5
* или если это hosted аккаунт, то (/a/ - должно быть!) https://mail.google.com/a/example.com/#all/124e3d1d452af489
*/
$domain = substr(strtolower(stristr($login, '@')), 1); //голый домен ёпте! без@
if ($domain == 'gmail.com' or $domain == 'googlemail.com'){/*$domain = 'google.com';*/ $preUrl = 'https://mail.google.com/mail/';}
else {$preUrl = 'https://mail.google.com/a/'.$domain;}
if (count($hexID) == 1 ){
echo "\nнайдена одна единственная цепочка писем, это ОЧЕНЬ гуд :) \n";
$finalUrl = $preUrl.'#all/'.$hexID[0];
echo "<a href='$finalUrl'>$finalUrl</a>";
return ($hexID ? $finalUrl : 'FAIL → ссылка $hexID');
}
if (count($hexID) > 1 ){
echo "\nнайдено несолько цепочек писем. применяем ярлык. ссылка должна быть на ярлык. ярлык это папка.
если применить ярлык к письму, то он будет применен ко всей цепочке.
Можно ли прменить ярлык к цепочке или надо применить его к каждому письму?\n";
/* если у всех писем один общий ярлык вида 'z/*' используй его и не добавляй новый ярлык - борьба с дубликатами.
* именно 'z/*' потому что писма могут быть объеденены например ярлыком статуса, или каким-то другим общим ярлыком.
* z это основной ярлык-папка с вложенными ярлыками, которые идут через дробь. Обязательно включить функцию вложенных
* ярлыков в gmail lab (экспериментальные функции) и добавить руками корневой ярлык и (?один вложенный?) на всякий случай.
*/
$labels = $protocol->fetch('X-GM-LABELS', $search); //вытащить все ярлыки из почты по данному поисковому запросу
//print_r($labels); echo "\n".count($labels);
foreach ($labels as $k => $v){
$str[] = '$labels['.$k.']';
}
$str = implode(', ', $str); // строка для сравнения массивов
eval('$sameLabels = array_intersect('.$str.');'); // сравни массивы и покажи ярлыки присутствующие у всех писем. С доп. (_assoc) проверкой не находит все повторения
///*это test*/array_push($sameLabels, 'z/9', 'z/14', 'z/15', 'z/18'); //не использовать $array[] = value - кривые ключи!
//echo '$sameLabels = '; print_r($sameLabels);
function findZlabel($val) {
if (($val{0}.$val{1}) == 'z/'){ //ярлыки для объединения начинаются с z/цифра...
return ($val);
}
}
$existingLabel = array_filter($sameLabels, "findZlabel"); //найди ярлыки вида z/*
//echo '$existingLabel = '; print_r($existingLabel);
if ($existingLabel = array_shift($existingLabel)){ // именно = а не == Ключи в массиве идут абы как т.к. использовали функцию array_intersect
$finalUrl = $preUrl.'#label/'.urlencode($existingLabel); /** #label/z%2F0 */
echo "\nтру → используем существующий ярлык\n";
echo "<a href='$finalUrl'>$finalUrl</a>";
return ($existingLabel ? $finalUrl : 'FAIL → используем существующий ярлык');
} else {
echo "\nнетру → создаём новый ярлык\n";
/** найдём какой использовать новый ярлык
* TODO: найти неиспользуемый ярлык и либо удалить его, либо использовать. Как это можно сделать:
* получаем список всех ярлыков, и проверям каждый на наличие в нём письма. Если ярлык ни где не испульзуется используем его.
* Проблема в том что это будет использовать много ресурсов учитывая то, что ярлыков будет дахуя > 1000
* Может быть имеет смысл $newLabel делать не из последнего ярлыка, а с первого, но опять же ресурсы, ярлыков будет дахуя > 1000
*/
$xlistz = $protocol->xlist("", "z/*");
$lastLabel = end(array_keys($xlistz));
$newLabel = $lastLabel;
while (array_key_exists($newLabel, $xlistz)) {
$newLabel = 'z/'.(str_replace('z/', '', $newLabel) + 1);
}
echo "\n\$newLabel = ".$newLabel;
// применить новый ярлык ко всем письмам по данному запросу и выдать URL
$store = $protocol->gmailLabel(array($newLabel), $search, null, '+', true);
//echo ($store ? "\ntrue" : "\nfalse");
$finalUrl = $preUrl.'#label/'.urlencode($newLabel); /** #label/z%2F0 */
echo "\n<a href='$finalUrl'>$finalUrl</a>";
return ($store ? $finalUrl : 'FAIL → используем новый ярлык');
}
}
?>
ну и пожалуй выложу класс который я написал:
<?php
/* 13.09.2011 можно спамить сюда testtesttest73@gmail.com это алиас рабочего ящика
это моя первая попытка написать класc пэхапэ дополняющий Zend_Mail_Protocol_Imap (версия $Id: Imap.php 18977 2009-11-14 14:15:59Z yoshida@zend.co.jp )
т.е. сам по себе этот клас не работает, только вместе с зендом.
require_once "Zend/Mail/Protocol/Imap.php";
первым делом добавим сюда hex2dec and vice versa
http://code.google.com/intl/ru-RU/apis/gmail/imap/
*/
class shkur_IMAP_class_Extends_Zend_Mail_Protocol_Imap extends Zend_Mail_Protocol_Imap {
/**
* set flags label to threads
*
* @param string|array $flags flags to set, add or remove - see $mode
* @param int $from message for items or start message if $to !== null
* @param int|null $to if null only one message ($from) is fetched, else it's the
* last message, INF means last message avaible
* @param string|null $mode 'null' and '+' to add flags, '-' to remove flags, everything else sets the flags as given
* @param bool $silent if false the return values are the new flags for the wanted messages
* @return bool|array new flags if $silent is false, else true or false depending on success
* @throws Zend_Mail_Protocol_Exception
*/
public function gmailLabel($flags, $from, $to = null, $mode = null, $silent = true){
$item = 'X-GM-LABELS';
($mode == null ? $mode = '+' : null);
if ($mode == '+' || $mode == '-') {
$item = $mode . $item;
}
$flags = $this->escapeList($flags);
if (is_array($from)) {
$set = implode(',', $from);
} else if ($to === null) {
$set = (int)$from;
} else if ($to === INF) {
$set = (int)$from . ':*';
} else {
$set = (int)$from . ':' . (int)$to;
}
$result = $this->requestAndResponse('STORE', array($set, $item, $flags), $silent);
if ($silent) {
return $result ? true : false;
}
$tokens = $result;
$result = array();
foreach ($tokens as $token) {
if ($token[1] != 'FETCH' || $token[2][0] != 'X-GM-LABELS') {
continue;
}
$result[$token[0]] = $token[2][1];
}
return $result;
}
/**
* Gets the parent stack of a string array element if it is found within the
* parent array
*
* This will not search objects within an array, though I suspect you could
* tweak it easily enough to do that
*
* @param string $child The string array element to search for
* @param array $stack The stack to search within for the child
* @return array An array containing the parent stack for the child if found,
* false otherwise
*/
function getParentStack($child, $stack) {
foreach ($stack as $k => $v) {
if (is_array($v)) {
// If the current element of the array is an array, recurse it and capture the return
$return = $this->getParentStack($child, $v);
// If the return is an array, stack it and return it
if (is_array($return)) {
return array($k => $return);
}
} else {
// Since we are not on an array, compare directly
if ($v == $child) {
// And if we match, stack it and return it
return array($k => $child);
}
}
}
// Return false since there was nothing found
return false;
}
/**
* Gets the complete parent stack of a string array element if it is found
* within the parent array
*
* This will not search objects within an array, though I suspect you could
* tweak it easily enough to do that
*
* @param string $child The string array element to search for
* @param array $stack The stack to search within for the child
* @return array An array containing the parent stack for the child if found,
* false otherwise
*/
function getParentStackComplete($child, $stack){
$return = array();
foreach ($stack as $k => $v) {
if (is_array($v)){
// If the current element of the array is an array, recurse it
// and capture the return stack
$stack = $this->getParentStackComplete($child, $v);
// If the return stack is an array, add it to the return
if (is_array($stack) && !empty($stack)){
$return[$k] = $stack;
}
} else {
// Since we are not on an array, compare directly
if ($v == $child){
// And if we match, stack it and return it
$return[$k] = $child;
}
}
}
// Return the stack
return empty($return) ? false: $return;
}
/** list переделанный в Xlist
* get mailbox list
*
* this method can't be named after the IMAP command 'LIST', as list is a reserved keyword
*
* @param string $reference mailbox reference for list
* @param string $mailbox mailbox name match with wildcards
* @return array mailboxes that matched $mailbox as array(globalName => array('delim' => .., 'flags' => ..))
* @throws Zend_Mail_Protocol_Exception
*/
public function xlist($reference = '', $mailbox = '*'){
$result = array();
$list = $this->requestAndResponse('XLIST', $this->escapeString($reference, $mailbox));
if (!$list || $list === true){
return $result;
}
foreach ($list as $item){
if (count($item) != 4 || $item[0] != 'XLIST'){
continue;
}
$result[$item[3]] = array('delim' => $item[2], 'flags' => $item[1]);
}
return $result;
}
/* Input: A decimal number as a String.
Output: The equivalent hexadecimal number as a String.*/
function dec2hex($number){
$hexvalues = array('0','1','2','3','4','5','6','7',
'8','9','a','b','c','d','e','f');
$hexval = '';
while($number != '0') {
$hexval = $hexvalues[bcmod($number,'16')].$hexval;
$number = bcdiv($number,'16',0);
}
return $hexval;
}
/* Input: A hexadecimal number as a String.
Output: The equivalent decimal number as a String. */
function hex2dec($number){
$decvalues = array('0' => '0', '1' => '1', '2' => '2',
'3' => '3', '4' => '4', '5' => '5',
'6' => '6', '7' => '7', '8' => '8',
'9' => '9', 'a' => '10', 'b' => '11',
'c' => '12', 'd' => '13', 'e' => '14',
'f' => '15');
$decval = '0';
$number = strrev($number);
for($i = 0; $i < strlen($number); $i++){
$decval = bcadd(bcmul(bcpow('16',$i,0),$decvalues[$number{$i}]), $decval);
}
return $decval;
}
/** так как эта функция будет исключена в следующих версиях Zend я пихну её сюда.
* do a search request
*
* This method is currently marked as internal as the API might change and is not
* safe if you don't take precautions.
*
* @internal
* @return array message ids array($this->escapeString($params)) - непонятная херня
*/
public function search(array $params){
$response = $this->requestAndResponse('SEARCH', $params);
if (!$response) {
return 'gotohell';//$response;
}
foreach ($response as $ids) {
if ($ids[0] == 'SEARCH') {
array_shift($ids);
return $ids;
}
}
return array();
}
}
?>