Geavanceerde e-mailfuncties bouwen met IMAP en PHP

Wat je gaat creëren

In deze tutorial zal ik je enkele voorbeelden uit de praktijk laten zien hoe je PHP en IMAP kunt gebruiken om nieuwe functies te bouwen voor het beheren van je e-mailfuncties die de grote e-mailproviders niet voor ons hebben gebouwd.

Mijn interesse hiervoor begon in 2010 toen ik Twaalf Gmail-ideeën schreef om e-mail te revolutioniseren (opnieuw), maar de ideeën die ik wilde hebben zijn grotendeels buiten bereik gebleven. Net zo belangrijk als e-mail is, de innovatie van e-mail als een toepassing is vrij traag.

We verdrinken in e-mail en het beheren van onze inboxen blijft een zware last. Maildiensten en klanten hebben heel weinig gedaan om ons hiermee te helpen. Het grootste deel van de e-mail die we ontvangen, wordt verzonden door machines, niet door mensen, en toch zijn wij degenen die dit allemaal afzonderlijk moeten verwerken. 

Analyse van mijn eigen e-mail toonde aan dat ik e-mail ontving van meer dan 230 geautomatiseerde afzenders, veel minder echte mensen. Ik was het zat om filters in Gmail te maken en een groot aantal afmeldingsformulieren in te vullen. Ik wilde meer controle hebben over het beheer van mijn e-mail en mijn leven vereenvoudigen.

Tot slot besloot ik het afgelopen jaar de functies te bouwen die ik nodig had. Het resultaat is Simplify Email (SE), een kleine web-app die u zelf kunt hosten en die een verscheidenheid aan coole nieuwe e-mailfuncties biedt die u allemaal kunt bekijken op de projectwebsite..

Het coolste aan SE is dat het een platform is voor het lezen, analyseren, routeren en beheren van je e-mail - de mogelijkheden zijn er in overvloed. Vereenvoudig e-mail is in wezen een programmeerbare speeltuin voor het "hacken" van uw eigen e-mail.

Ik zal je door de code van drie voorbeelden van SE leiden die PHP, IMAP en MySQL gebruiken om met e-mail te werken:

  1. Uw Postvak IN controleren en berichten filteren
  2. Een Whitelist-uitdaging implementeren voor onbekende afzenders
  3. Onbeantwoorde e-mail melden

Deze tutorial zal je zeker een voorsprong geven bij het schrijven van IMAP-code in PHP. Maar je kunt ook direct werken met de codebase Vereenvoudig Email. Je kunt de code kopen voor slechts $ 10, en er is een oudere open source-versie (die enkele van de functies mist die we hieronder beschrijven). Installatiehandleidingen zijn bedoeld voor typische Linux-configuraties. Ik bied ook voorgeïnstalleerde foto's bij Digital Ocean voor $ 25 en een handheld valet-installatie. SE is geschreven in PHP, in het Yii Framework. 

Merk op dat je via je lokale ontwikkelmachine geen toegang kunt krijgen tot de meeste e-mailservers, tenzij je een veilige IMAP-bibliotheek compileert voor PHP. Dit is een van de redenen waarom ik mensen aanmoedig om Simplify Email in druppeltjes op Digital Ocean uit te voeren. Er zijn ook een paar trucjes om Google-accountbeveiliging te krijgen om je via IMAP binnen te laten.

Werken met IMAP

Hoe Simplify Email werkt

Met SE kunt u uw e-mailclient naar keuze blijven gebruiken op internet en op uw mobiele apparaten. U hoeft geen apps of persoonlijke gewoonten te wijzigen. SE heeft toegang tot uw e-mailaccounts achter de schermen via IMAP; SE werkt als een slimme persoonlijke assistent, pre-processen uw e-mail, bewegende berichten naar de juiste plaatsen op basis van alles wat je het hebt verteld.

Wanneer een bericht van een bekende afzender aankomt, verplaatst SE het naar de map die u hebt opgegeven. Wanneer iemand voor de eerste keer van een onbekende afzender arriveert, wordt deze naar de beoordelingsmap verplaatst. 

Om de paar uur (of op een frequentie die u kiest), stuurt SE u een samenvatting van waar het uw berichten heeft verplaatst en welke berichten in de beoordeling zijn. Let op, koppelingen voor trainingszenders zijn opgenomen in de beoordelingsmap, waardoor het vrij eenvoudig is om SE in de loop van de tijd te trainen.

U kunt op elk gewenst moment door uw beoordelingsmap bladeren - u hoeft niet te wachten tot de samenvatting is binnengekomen. Maar het voordeel van SE is dat u niet langer door uw mappen hoeft te bladeren; u kunt gewoon uw samenvatting lezen om een ​​zicht te krijgen op de e-mail die u hebt ontvangen en om nieuwe afzenders op te leiden.

1. Uw Postvak IN controleren en berichten filteren

SE gebruikt verschillende cron-taken om op de achtergrond op uw server te werken. Elke wordt gebeld DaemonController.php.

De eerste, processInbox, wordt vaak genoemd en moet snel werken. Het is zijn taak om e-mail te controleren en zo snel mogelijk uit de Inbox te verwijderen in de triage-map, de filtermap genaamd. 

De seconde, processFiltering, is procesintensiever en voert diepere bewerkingen uit op e-mail, waardoor uiteindelijk berichten naar hun eindbestemming worden verplaatst.

De methode ProcessInbox

De cron-taken aanroepen processInbox regelmatig:

public function actionInbox () // verplaatst inbox-berichten naar @filtering // wordt vaak uitgevoerd $ r = new Remote (); $ R-> processInbox (); 

Voor elk account ontsleutelen we uw e-mailreferenties en gebruiken we imap_open om een ​​IMAP-stream te maken naar uw postvak IN:

openbare functie open ($ account_id, $ mailbox = ", $ options = NULL) // opent map in een IMAP-account $ account = Account :: model () -> findByPk ($ account_id); $ this-> hostname = $ account-> adres; if (! stristr ($ this-> hostname, '')) $ this-> hostname = ''. $ this-> hostname. ''; $ cred = Account :: model () -> getCredentials ($ account-> cred); if ($ account-> provider == Account :: PROVIDER_ICLOUD) // icloud accepteert alleen een deel van de mailbox, bijvoorbeeld stevejobs vs. [email protected] $ temp = explode (' @ ', $ cred [0]); $ cred [0] = $ temp [0]; $ this-> stream = imap_open ($ this-> hostname. $ mailbox, $ cred [0], $ cred [1 ], $ options, 1) of sterven ('Can not connect to mail server - account_id:'. $ account_id. ". print_r (imap_errors ()));  

Binnen processInbox, we gebruiken PHP-bibliotheekfuncties imap_search en imap_fetch_overview om een ​​reeks berichten op te halen:

// lookup folder_id van de INBOX van dit account $ folder_id = Map :: model () -> lookup ($ account_id, $ this-> path_inbox); $ This-> geopend ($ account_id, $ this-> path_inbox); $ Cnt = 0; $ message_limit = 50; // break after n messages om time-out echo te voorkomen 'Sorteer sinds:' .date ("j F Y", $ tstamp); // imap_search datumnotatie 30 november 2013 $ recent_messages = @imap_search ($ this-> stream, 'SINCE' '.date ("j F Y", $ tstamp).' "', SE_UID); als ($ recent_messages === false) doorgaan; // te doen - ga verder naar de volgende account $ result = imap_fetch_overview ($ this-> stream, implode (',', array_slice ($ recent_messages, 0, $ message_limit)), FT_UID); 

Vervolgens verwerken we de reeks berichten in de inbox:

foreach ($ result as $ item) if (! $ this-> checkExecutionTime ($ time_start)) pauze; // krijg msg header en stream uid $ msg = $ this-> parseHeader ($ item); 

Hier is een aangepaste versie van openbaar beschikbare IMAP-header-ontleedcode die de aanvullende informatie verzamelt die SE nodig heeft voor een verscheidenheid aan taken. In principe gebruikt het imap_rfc822_parse_adrlist om ontvangerinformatie, bericht-id, onderwerp en tijdstempels (of afzenderinformatie bij het scannen van de verzonden map) te bepalen:

 openbare functie parseHeader ($ header) // parses header-object geretourneerd door imap_fetch_overview if (! isset ($ header-> from)) return false;  else $ from_arr = imap_rfc822_parse_adrlist ($ header-> from, 'gmail.com'); $ fi = $ from_arr [0]; $ msg = array ("uid" => (isset ($ header-> uid))? $ header-> uid: 0, "personal" => (isset ($ fi-> personal))? @ imap_utf8 ($ fi -> persoonlijk): "", "email" => (isset ($ fi-> mailbox) && isset ($ fi-> host))? $ fi-> mailbox. "@". $ fi-> host: " "," mailbox "=> (isset ($ fi-> mailbox))? $ fi-> mailbox:" "," host "=> (isset ($ fi-> host))? $ fi-> host:" "," onderwerp "=> (isset ($ header-> onderwerp))? @ imap_utf8 ($ header-> subject):" "," message_id "=> (isset ($ header-> message_id))? $ header- > message_id: "", "in_reply_to" => (isset ($ header-> in_reply_to))? $ header-> in_reply_to: "", "udate" => (isset ($ header-> udate))? $ header- > udate: 0, "date_str" => (isset ($ header-> date))? $ header-> date: ""); // verwerkt fetch met uid en rfc header parsing if ($ msg ['udate'] == 0 && isset ($ header-> date)) $ msg ['udate'] = strtotime ($ header-> date);  $ msg ['rx_email'] = "; $ msg ['rx_personal'] ="; $ msg ['rx_mailbox'] = "; $ msg ['rx_host'] ="; if (isset ($ header-> to)) $ to_arr = imap_rfc822_parse_adrlist ($ header-> to, 'gmail.com'); $ to_info = $ to_arr [0]; if (isset ($ to_info-> mailbox) && isset ($ to_info-> host)) $ msg ['rx_email'] = $ to_info-> mailbox. '@'. $ to_info-> host;  if (isset ($ to_info-> personal)) $ msg ['rx_personal'] = $ to_info-> persoonlijk; if (isset ($ to_info-> mailbox)) $ msg ['rx_mailbox'] = $ to_info-> mailbox; if (isset ($ to_info-> host)) $ msg ['rx_host'] = $ to_info-> host;  return $ msg; 

We maken records voor de afzender en de berichtenveloppe in onze database:

 // sla systeemberichten over als ($ msg ['email'] == $ system_email) doorgaan; // als udate te oud is, sla dan msg over als (time () - $ msg ['udate']> $ this-> scan_seconds) doorgaan; // skip msg // standaardactie $ action = self :: ACTION_MOVE_FILTERED; $ isNew = $ s-> isNew ($ account_id, $ msg ["email"]); // zoek verzender op, maak ze nieuw, $ sender_id = $ s-> add ($ user_id, $ account_id, $ msg ["persoonlijk"], $ msg ["mailbox"], $ msg ["host"], 0); $ afzender = Afzender :: model () -> findByPk ($ afzender_id); // maak een bericht in db indien nodig $ message_id = $ m-> add ($ user_id, $ account_id, 0, $ sender_id, $ msg ['message_id'], $ msg ['subject'], $ msg ['udate '], $ msg [' in_reply_to ']); $ message = Bericht :: model () -> findByPk ($ message_id); 

Als de afzender nieuw voor ons is (onbekend), sturen we een e-mail met een witte lijst met uitdagingen (we zullen meer praten over de uitdagingen van de witte lijst in de volgende sectie hieronder):

if ($ isNew) $ this-> challengeSender ($ user_id, $ account_id, $ sender, $ message); 

Vervolgens bepalen we of de gebruiker mogelijk een bericht uit een andere map naar de inbox heeft gesleept, met de bedoeling het via slepen en neerzetten te trainen. Als dit het geval is, stellen we de training voor deze afzender in op de inbox. Met andere woorden, de volgende keer willen we alleen berichten van deze afzender naar de inbox leiden:

 if ($ message ['status'] == Bericht :: STATUS_FILTERED || $ message ['status'] == Bericht :: STATUS_REVIEW || ($ message ['status'] == Bericht :: STATUS_TRAINED && $ message [ 'folder_id'] <> $ folder_id) || ($ message ['status'] == Bericht :: STATUS_ROUTED && $ message ['folder_id'] <> $ folder_id)) // then it's a training $ action = self :: ACTION_TRAIN_INBOX;  else if (($ message ['status'] == Bericht :: STATUS_TRAINED || $ message ['status'] == Bericht :: STATUS_ROUTED) && $ message ['folder_id'] == $ folder_id) // als je al bent getraind of naar de inbox bent gerouteerd, sla je deze over $ action = self :: ACTION_SKIP; echo 'Trained previous, skip'; lb (); doorgaan met;  

Zo niet, dan bereiden we ons voor om het bericht naar de map Filtering te verplaatsen voor verdere verwerking. Ten eerste kunnen we meldingen naar de telefoon van de gebruiker verzenden als er een afzenderovereenkomst of zoekwoordovereenkomst voor meldingen is (en het is geen rustige uren):

 if ($ action == self :: ACTION_MOVE_FILTERED) $ cnt + = 1; if ($ sender-> exclude_quiet_hours == Sender :: EQH_YES or! $ this-> isQuietHours ($ user_id)) // stuur smartphone-meldingen op basis van afzender if ($ afzender-> alert == Afzender :: ALERT_YES) $ this-> aanmelden ($ afzender, $ message, Monitor :: NOTIFY_SENDER);  // stuur meldingen op basis van sleutelwoorden als (AlertKeyword :: model () -> scan ($ msg)) $ this-> notify ($ sender, $ message, Monitor :: NOTIFY_KEYWORD);  // verplaats imap msg naar + Filtering echo 'Verplaatsen naar + filteren'; lb (); // $ result = @imap_mail_move ($ this-> stream, $ msg ['uid'], $ this-> path_filtering, CP_UID); $ result = $ this-> messageMoveHandler ($ msg ['uid'], $ this-> path_filtering, false); if ($ result) echo 'verplaatst
'; $ M-> setStatus ($ message_id, Message :: STATUS_FILTERED);

Als het bericht naar de inbox is gesleept, werken we onze trainingsinstellingen bij:

else if ($ action == self :: ACTION_TRAIN_INBOX) // stel verzender folder_id in in inbox echo 'Train to Inbox'; lb (); $ M-> setStatus ($ message_id, Message :: STATUS_TRAINED); / / alleen trein afzender wanneer bericht is nieuwer dan laatste instelling als ($ msg ['udate']> = $ afzender ['last_trained']) $ s-> setFolder ($ afzender_id, $ map_id); 

De methode ProcessFiltering

De secundaire verwerkingsmethode wordt aangeroepen processFiltering, ook in DaemonController.php. Het doet de meer tijdrovende aspecten van het verplaatsen van berichten naar de juiste mappen:

public function actionIndex () // verwerkt berichten in @Filtering naar geschikte mappen $ r = new Remote (); $ R-> processFiltering (); // Tijdstempel van cronjob opnemen voor monitoring $ file = file_put_contents ('./ protected / runtime / cronstamp.txt', time (), FILE_USE_INCLUDE_PATH);  

Met deze methode wordt uw e-mailaccount geopend om te zoeken naar recente berichten en gegevens over hen te verzamelen. Het gebruikt ook imap_search, imap_fetch_overview en parseHeader:

$ tstamp = time () - (7 * 24 * 60 * 60); // 7 dagen geleden $ recent_messages = @imap_search ($ this-> stream, 'SINCE "' .date (" j F Y ", $ tstamp). '"', SE_UID); als ($ recent_messages === false) doorgaan; // te doen - ga verder naar de volgende account $ result = imap_fetch_overview ($ this-> stream, implode (',', array_slice ($ recent_messages, 0, $ message_limit)), FT_UID); foreach ($ resultaat als $ item) $ cnt + = 1; if (! $ this-> checkExecutionTime ($ time_start)) pauze; // krijg msg header en stream uid $ msg = $ this-> parseHeader ($ item); 

De primaire verwerkingslus voor elk bericht in de filtermap is behoorlijk gedetailleerd. Eerst kijken we naar het adres van de ontvanger, aangezien SE mensen toestaat om mappen te trainen op adres van de ontvanger, bijvoorbeeld berichten naar het happyvegetarian.com-domein gaan naar de vegetarische map:

 // Stel de standaardactie in om naar de revisiemap $ action = self :: ACTION_MOVE_REVIEW; $ destination_folder = 0; // zoek op en creëer ontvanger $ recipient_id = $ r-> add ($ user_id, $ account_id, $ msg ['rx_email'], 0); $ routeByRx = $ this-> routeByRecipient ($ recipient_id); if ($ routeByRx! == false) $ action = $ routeByRx-> actie; $ destination_folder = $ routeByRx-> destination_folder;  

Vervolgens zoeken we de afzender op en maken we een nieuw record in de database (indien nodig). Als er een training bestaat voor de afzender, kunnen we de doelmap instellen:

 // zoek verzender op, maak ze nieuw, $ sender_id = $ s-> add ($ user_id, $ account_id, $ msg ["persoonlijk"], $ msg ["mailbox"], $ msg ["host"], 0); $ afzender = Afzender :: model () -> findByPk ($ afzender_id); // indien bekende afzenderbestemming, route naar map if ($ destination_folder == 0 && $ afzender ['folder_id']> 0) $ action = self :: ACTION_ROUTE_FOLDER; $ destination_folder = $ afzender ['folder_id'];  

Als een niet-getrainde (nieuwe) afzender zichzelf heeft geverifieerd via een Whitelist-uitdaging (die we in de volgende sectie hieronder bespreken), leiden we dit bericht naar de inbox:

// witte lijst geverifieerde afzenders gaan naar inbox als ($ afzender-> is_geverifieerd == 1 && $ afzender ['folder_id'] == 0 && UserSetting :: model () -> useWhitelisting ($ user_id)) // plaats bericht in inbox $ action = self :: ACTION_ROUTE_FOLDER; $ destination_folder = Folder :: model () -> lookup ($ account_id, $ this-> path_inbox); 

Vervolgens maken we een berichtvermelding in de database met de envelopinformatie over dit bericht:

 // maak een bericht in db $ message = Message :: model () -> findByAttributes (array ('message_id' => $ msg ['message_id'])); if (! empty ($ message)) // message exists already, $ message_id = $ message-> id;  else $ message_id = $ m-> add ($ user_id, $ account_id, 0, $ sender_id, $ msg ['message_id'], $ msg ['subject'], $ msg ['udate'], $ msg [ 'als antwoord op']);  

Als het afkomstig is van een onbekende, niet-geverifieerde afzender, kunnen we het bericht verplaatsen naar de revisiemap. De revisiemap bevat alle berichten van afzenders die we niet herkennen.

Als het bericht afkomstig is van een bekende afzender en we een bestemming in gedachten hebben, kunnen we deze verplaatsen zolang het geen rustige uren zijn (en niet storen is uitgeschakeld):

 if ($ recipient_id! == false) $ m-> setRecipient ($ message_id, $ recipient_id); if ($ action == self :: ACTION_MOVE_REVIEW) echo 'Verplaatsen naar + filteren / reviewen'; lb (); // $ result = @imap_mail_move ($ this-> stream, $ msg ['uid'], $ this-> path_review, CP_UID); $ result = $ this-> messageMoveHandler ($ msg ['uid'], $ this-> path_review, false); if ($ result) echo 'verplaatst
'; $ M-> setStatus ($ message_id, Message :: STATUS_REVIEW); else if ($ action == self :: ACTION_ROUTE_FOLDER || $ action == self :: ACTION_ROUTE_FOLDER_BY_RX) // mapnaam opzoeken door map_id $ map = Map :: model () -> findByPk ($ destination_folder); // if inbox & quiet hours, routeplanner nu niet (strtolower ($ folder ['name']) == 'inbox' en $ sender-> exclude_quiet_hours == Sender :: EQH_NO en $ this-> isQuietHours ( $ user_id)) ga verder; echo 'Verhuizen naar'. $ folder ['name']; lb (); $ mark_read = Map :: model () -> isMarkRead ($ folder ['mark_read']) || Sender :: model () -> isMarkRead ($ afzender [ 'mark_read']); // $ result = @imap_mail_move ($ this-> stream, $ msg ['uid'], $ folder ['name'], CP_UID); $ result = $ this-> messageMoveHandler ($ msg ['uid'], $ folder ['name'], $ mark_read); if ($ result) echo 'verplaatst
'; $ M-> setStatus ($ message_id, Message :: STATUS_ROUTED); $ M-> setFolder ($ message_id, $ destination_folder);

Tijdens rustige uren worden berichten voornamelijk in de filtermap bewaard.

Om de paar uur bouwt een ander proces de berichtsamenvatting op met behulp van de berichtentabelrecords om te bepalen welke e-mails onlangs zijn ontvangen en gefilterd en hoe ze zijn gerouteerd.

2. Een Whitelist-uitdaging implementeren voor onbekende afzenders

Het doel van de whitelist-uitdaging is om elke boodschap van een onbekende afzender, b.v. mogelijk een marketingbot of -spammer uit je inbox. SE plaatst e-mails van onbekende afzenders in de beoordelingsmap. Als u echter de witte lijst inschakelt, sturen we een uitdagings-e-mail die de afzender een kans geeft om te verifiëren dat deze persoon een persoon is. Als ze reageren, verplaatsen we het bericht naar je inbox. Als de e-mail ongewenst blijkt te zijn, kunt u het bericht uit de samenvatting wegzippen of naar een map slepen waarnaar u het wilt trainen..

De gebruiker kan de witte lijst in- en uitschakelen in instellingen:

Om whitelisting te implementeren, sturen we e-mailuitdagingen wanneer e-mail van een nieuwe afzender komt:

if ($ isNew) $ this-> challengeSender ($ user_id, $ account_id, $ sender, $ message); 

ChallengeSender stuurt een gecodeerde link naar de gebruiker zodat deze kan klikken. We hebben ook enkele beveiligingen om ervoor te zorgen dat we ons niet in een e-maillus vastlopen met een afwezigheidsbericht:

 public function challengeSender ($ user_id, $ account_id, $ sender, $ message) // whitelist email challenge $ yg = new Yiigun (); $ ac = Account :: model () -> findByPk ($ account_id); if (! empty ($ ac ['challenge_name'])) $ from = $ ac ['challenge_name']. ' mg_domain '>'.; else $ from = 'Filter mg_domain '>'.; $ cred = Account :: model () -> getCredentials ($ ac-> cred); $ account_email = $ cred [0]; unset ($ cred); // veiligheid: controleert geen recente e-mail als ($ afzender-> last_emailed> (tijd () - (48 * 60 * 60))) false retourneert; if ($ afzender-> isBot ($ afzender ['e-mail'])) // om te doen - kan deze persoon ook standaard instellen op bulk retourneer false;  $ link = Yii :: app () -> getBaseUrl (true). "/ verzender / verify / s /".$ afzender-> id." / m /".$ bericht-> id. '/ u /' . $ bericht-> udate; $ subject = 'Verifieer alstublieft het bericht dat u naar'. $ account_email; $ Body ="

Hoi,

Ik probeer ongevraagde e-mail te verminderen. Kunt u alstublieft uw e-mailadres verifiëren door op de onderstaande link te klikken:
'$ Link.'

Als u uw e-mailadres verifieert, wordt uw bericht sneller verzonden naar mijn postvak IN. Bedankt voor je assistentie!

'; $ yg-> send_html_message ($ from, $ afzender ['email'], $ subject, $ body); // update last_emailed $ afzender-> touchLastEmailed ($ afzender-> id);

Als de ontvanger vervolgens op de gecodeerde link klikt, controleren we deze in de database. De afzendercontroller verwerkt deze verzoeken en controleert hun geldigheid:

 openbare functie actionVerify ($ s = 0, $ m = 0, $ u = 0) // verifieert dat beveiligde MSG-URL van digest geldig is, meldt zich aan, toont msg $ sender_id = $ s; $ message_id = $ m; $ udate = $ u; $ msg = Bericht :: model () -> findByPk ($ message_id); if (! empty ($ msg) && $ msg-> sender_id == $ sender_id && $ msg-> udate == $ udate) $ result = 'Bedankt voor uw hulp. Ik zal zo snel mogelijk reageren op uw e-mail. '; $ a = nieuw Geavanceerd (); $ A-> verifySender ($ msg-> account_id, $ SENDER_ID);  else $ result = 'Sorry, we kunnen uw e-mailadres niet verifiëren.';  $ this-> render ('verify', array ('result' => $ result,)); 

Dit vertelt onze verwerkingslussen om deze en toekomstige berichten van deze afzender naar de inbox te verplaatsen.

3. Onbeantwoorde e-mail melden

Soms helpt het om een ​​overzicht te zien van berichten die u hebt verzonden, maar waarop u geen antwoord heeft ontvangen. Om deze te identificeren, controleert Simplify Email berichten die zijn verzonden maar nog geen antwoord hebben ontvangen.

Elk bericht dat we ontvangen bevat een unieke id, genaamd message_id (onderdeel van de IMAP-specificatie). Het ziet er vaak als volgt uit:

Message-Id: 

Bovendien, wanneer berichten worden verzonden als antwoord op andere berichten, hebben ze een als antwoord op veld dat terug linkt naar het origineel message_id.

We gebruiken dus een SQL-query om alle ontvangen berichten te vinden die geen overeenkomend antwoordbericht hebben dat naar hun verwijst message_id. Hiervoor gebruiken we een LINKER OUTER JOIN waar er geen is als antwoord op ID kaart:

openbare functie getUnanswered ($ account_id, $ mode = 0, $ range_days = 7) if ($ mode == 0) $ subject_compare = 'not'; else $ subject_compare = "; $ query = Yii :: app () -> db-> createCommand (" SELECT fi_sent_message.id, fi_sent_message.recipient_id als sender_id, fi_sent_message.subject, fi_sent_message.udate, fi_message.in_reply_to, fi_sent_message.message_id FROM fi_sent_message LEFT OUTER JOIN fi_message ON fi_message.in_reply_to = fi_sent_message.message_id WHERE fi_sent_message.account_id = ". $ account_id." AND fi_message.in_reply_to is null en fi_sent_message.udate> ". (time () - (3600 * 24 * $ range_days) ). "en fi_sent_message.subject". $ subject_compare. "zoals 'Re:%' ORDER BY fi_sent_message.udate DESC") -> queryAll (); return $ query;

Wij gebruiken de $ subject_compare modus om onderscheid te maken tussen onze verzonden berichten die niet zijn beantwoord en onze verzonden antwoorden op een thread die niet zijn beantwoord. Dit is het onbeantwoorde berichtrapport in uw account:

SE biedt deze informatie ook als een optionele samenvatting, het zogenaamde onbeantwoord e-mailoverzicht. Je kunt het elke dag, om de paar dagen of elke week ontvangen.

We gebruiken ook een soortgelijke SQL-tabel met Google Charts om rapporten te geven over hoe vaak bepaalde mensen u e-mailen:

 public function reportInbound ($ account_id, $ range = 30, $ limit = 100) $ result = Yii :: app () -> db-> createCommand ('SELECT fi_sender.personal, fi_sender.email, count (sender_id) as cnt FROM fi_message LEFT JOIN fi_sender ON fi_sender.id = fi_message.sender_id WHERE fi_sender.account_id =: account_id AND fi_message.created_at> DATE_SUB (NU (), INTERVAL: bereik DAY) GROUP BY sender_id BESTELLING BY cnt desc LIMIT: limit ') -> bindValue ( 'range', $ range) -> bindValue ( 'ACCOUNT_ID', $ account_id) -> bindValue ( 'limit', $ limiet) -> queryAll (); return $ resultaat;  

Ik zal binnenkort meer over Google Charts voor Tuts + gaan schrijven. 

Volgende stappen

Ik hoop dat je Simplify-e-mail intrigerend genoeg hebt gevonden om PHP IMAP-programmering uit te proberen. Er zijn zoveel coole functies die je kunt bouwen zonder dat de grote e-mailproviders iets nieuws hoeven te doen.

Als je vragen of correcties hebt, plaats deze dan in de comments. Als je mijn toekomstige Tuts + tutorials en andere series wilt volgen, volg dan @reifman of bezoek mijn auteurspagina. U kunt hier ook contact met mij opnemen.

Gerelateerde Links

Hier zijn enkele extra links die u mogelijk handig vindt:

  • Vereenvoudig e-mail
  • Introductie om e-mail te vereenvoudigen (video)
  • Twaalf Gmail-ideeën om e-mail te revolutie (opnieuw) 
  • Dekking van Simplify Email in BoingBoing hier en hier
  • PHP IMAP-referentie
  • Introductie van het Yii Framework (Tuts +)