Ayuda para escribir AppleScript con regla de correo para mantener la más reciente

Creé una regla para mover el correo de remitentes específicos a un nuevo buzón llamado "Noticias". Me gustaría que mi AppleScript pasara por la carpeta "Noticias" y solo mantuviera el emaisa más reciente de esos remitentes, eliminando los mensajes anteriores. (Esto es como una de las funciones de barrido en MSLive/Outlook). Intenté escribir un script de la siguiente manera, que es invocado por la regla:

using terms from application "Mail"
    on perform mail action with messages these_messages for rule this_rule
        tell application "Mail"
            set dateToday to current date
            set TargetInbox to mailbox "News" of account "iCloud"
            set EveryMessage to every message of TargetInbox
            repeat with eachMessage in these_messages
                repeat with this_message in EveryMessage
                    set eachSender to the sender of eachMessage
                    set this_sender to the sender of this_message
                    set messageDate to date received of EveryMessage
                    if messageDate < dateToday and eachSender = this_sender then
                        delete this_message
                    end if
                end repeat
            end repeat
        end tell
    end perform mail action with messages
end using terms from

Veo que la secuencia de comandos es llamada por la regla, debido al engranaje giratorio en la barra de menú, pero no se produce ninguna de las eliminaciones deseadas, y la secuencia de comandos parece colgarse en una especie de bucle.

Soy un AppleScript n00b, lo hago mucho mejor con JavaScript. Agradecería cualquier ayuda.

Gracias, Betalant

macos 10.13.5 | Correo 11.4 | AppleScript 2.7

Voy a echar un vistazo más extensamente más adelante. Pero lo que me gusta hacer es probar cada parte de un guión. Por ejemplo, puede usar display dialog "Hi", que es como el equivalente de JavaScript de alerta ("Hola"), en las repeticiones para verificar si reaparecen correctamente. Luego cambia Hola con una variable que usa (como this_message) para asegurarse de que la variable sea correcta.
Gran consejo, para usar display dialogcomo una especie de console.logdepuración. Le daría una oportunidad.
También hay una opción de consola en AppleScript pero siempre preferí los diálogos. :)

Respuestas (3)

No uso Mail.app y no tengo mucha experiencia en la implementación de acciones de Mail , por lo que no estoy en condiciones de probar su script. Sin embargo, al leerlo, hay lugares en él de posible preocupación que me gustaría mirar primero al depurar.

¿Qué tamaño tiene su buzón de "Noticias" ?

Hay dos características en su secuencia de comandos que amenazan con colgar o expirar el tiempo de espera:

1.set EveryMessage to every message of TargetInbox

Si ese buzón tiene miles o incluso cientos de correos electrónicos, la recuperación every messagecomo una lista de messageobjetos a los que se hace referencia individualmente (que es lo que hace esta declaración) podría llevar algún tiempo. Casi siempre es mucho más eficiente y menos intensivo en el uso del procesador y la memoria para almacenar a reference toel every messageobjeto (colección):

set EveryMessage to a reference to every message of the TargetInbox

Tiene otra característica beneficiosa que abordaré más adelante.

repeat2. Bucles anidados potencialmente exigentes

Los bucles anidados son a veces un mal necesario, pero la consulta sobre el tamaño del buzón es, nuevamente, una fuente de ansiedad para mí, especialmente porque se encuentra en el corazón del nido donde se realiza la mayor parte de las operaciones. Esto significa que, para cada nuevo mensaje que activa el script, cada uno de los mensajes del buzón debe comprobarse y compararse con el mensaje activador.

Estas son dos ramas de un mismo problema, realmente, porque si su buzón solo tiene 5 mensajes, nada de esto es particularmente molesto. De lo contrario, primero obtendrá AppleScript para recuperar todos los mensajes en el buzón y almacenarlos en una variable; luego recorre esta carga potencialmente pesada y compara cada elemento con otro.

Si no tuvo más remedio que usar un repeatbucle sabiendo que tendría que iterar a través de muchos elementos de la lista (una vez que supere los 500 de cualquier cosa, AppleScript puede comenzar a fallar), puede ayudar, una vez más, pasar AppleScript un referencia a la lista de elementos en lugar de la lista real. Pasar cualquier referencia a través de un objeto de secuencia de comandos (que es lo que a reference toesencialmente se hace) es mucho, mucho más rápido que hacer que AppleScript acceda a un objeto por otros medios. Aquí hay una excelente explicación y demostración de estos principios .

Cambios simples y correcciones de errores.

Después de abordar el gran problema anterior, que podría resultar de poca relevancia para su caso particular, aquí hay uno o dos elementos de facto que se pueden cambiar fácilmente para mejorar la eficiencia de la secuencia de comandos o evitar que arroje un error:

1. Colocación de declaraciones de variables

set eachSender to the sender of eachMessage

Esta línea se declara actualmente dentro del repeatbucle interno, que también resulta ser el más carnoso de los dos, en términos generales hipotéticos. Sin embargo, lo que está haciendo es establecer el valor de la variable eachSenderen el mismo valor para tantos mensajes que tenga en su buzón. Esas son muchas operaciones desperdiciadas para AppleScript (un total del número de these_messages multiplicado por el número de EveryMessage).

En su lugar, muévelo aquí:

repeat with eachMessage in these_messages
    set eachSender to the sender of eachMessage  # ...now it's here
    repeat with this_message in EveryMessage
        # It was here...
        .
        .
        .
    end repeat
end repeat

Ahora AppleScript solo tiene que establecer la variable una vez eachMessageen these_message. ¡Esa es una mejora de la eficiencia del tiempo polinomial hasta el tiempo lineal!

2.set messageDate to date received of EveryMessage

Inicialmente, pensé que estaba tratando de acceder al EveryMessageobjeto como una referencia a la colección de la que hablé anteriormente, y simplemente no me di cuenta de que no podía hacerlo de la forma en que declaró EveryMessage.

Pero ahora me doy cuenta de que lo más probable es que solo tus manos escribieran más rápido que tus pensamientos e introdujeran un molesto error tipográfico. EveryMessageaquí debería ser this_message, es decir:

set messageDate to date received of this_message

Esto me lleva muy bien a mi punto final, que insinué anteriormente, con respecto al otro beneficio de declarar una variable a una colección de objetos como referencia.

Si hubieras hecho esto:

set EveryMessage to a reference to every message of the TargetInbox

luego esto:

set messageDate to date received of EveryMessage

sería una pieza perfectamente válida de AppleScript, que le permite acceder a la propiedad de cada uno de los elementos de una lista IFF a través de una referencia a esa lista antes de que se produzca cualquier tipo de desreferenciación. Una vez que se elimina la referencia, la lista se evalúa en referencias de objetos individuales y ya no le dará los medios para enumerar a través de sus propiedades de esa manera.

Lo que obtiene si se hace correctamente es una lista que contiene los valores de la date receivedpropiedad de every message of the TargetInbox, todo a través de una sola línea de AppleScript.

Actualmente, como no fue así como lo implementó, la declaración definitivamenteset messageDate generará un error.

Esto explica por qué no se produce ninguna de las eliminaciones, porque su secuencia de comandos nunca alcanzará con éxito esa parte. Sin embargo, el error ocurriría de inmediato, por lo que creo que el script ni siquiera llega a esa línea, ni siquiera una vez. Por lo tanto, el problema debe originarse en un punto de la secuencia de comandos antes de que entre con éxito en el repeatbucle interno. Y ya conoces mi corazonada al respecto.

Ahora para una solución potencial

Lamento no poder probar mi secuencia de comandos para verificar que definitivamente funcionará la primera vez sin ningún ajuste, así que dime cómo te va con esto:

    using terms from application "Mail"
        on perform mail action with messages these_messages
            tell application "Mail"
                set everyMessage to a reference to messages in mailbox "News" of account "iCloud"
                repeat with eachMessage in these_messages
                    delete (everyMessage where ¬
                        the date received is not the (current date) ¬
                        and its sender is the sender of eachMessage)
                end repeat
            end tell
        end perform mail action with messages
    end using terms from

Como puede ver, eliminé por completo el repeatbucle más interno gracias al uso del a reference tooperador. Como dije everyMessagede esa manera, puedo (con suerte) enumerar las propiedades de todos los elementos que contiene la lista de una sola vez, lo que debería ser aproximadamente infinitas veces más rápido que tener que verificar la propiedad de cada elemento individualmente.

¡Muchas gracias a @CJK por no solo ofrecer una buena solución, sino por explicar tan detalladamente que aprendí aspectos valiosos de AppleScript! Vea mi comentario en esta publicación para mi respuesta completa.
@LantzWarrick Siempre es bueno obtener la perspectiva de los segundos codificadores, incluso si su código funciona. Es posible que tengan un enfoque diferente que sea mejor, e incluso si no es mejor, verá una forma diferente de resolver el problema. A menudo hay muchas, muchas maneras de resolver un problema. ¡Sigue aprendiendo AppleScript, siempre podemos usar codificadores creativos!
"debería ser aproximadamente infinitas veces más rápido" 😂😂😂 👍

Muchas gracias a @CJK por una respuesta tan completa. Su solución funciona con solo un ajuste y también aprendí algunos conceptos básicos para AppleScript. El siguiente script de trabajo ahora se ejecuta con bastante rapidez:

using terms from application "Mail"
    on perform mail action with messages these_messages for rule this_rule
        tell application "Mail"
            set everyMessage to a reference to messages in mailbox "News" of account "iCloud"
            repeat with eachMessage in these_messages
                delete (everyMessage where ¬
                    the date received is not the (current date) ¬
                    and its reply to is the reply to of eachMessage)
            end repeat
        end tell
    end perform mail action with messages
end using terms from

La secuencia de comandos parecía no ejecutarse sin la adición en la línea 2 for rule this_ruley supongo que es necesario para pasar el these_messagesobjeto que es el resultado del filtrado definido en la regla principal mail.app que invoca la secuencia de comandos.

(Al final del repeatciclo, cambié sendera reply topara manejar las instancias en las que el remitente había cambiado su cadena de autoidentificación en la cuenta de envío, aunque la dirección real seguía siendo la misma, lo que provocó que el script no viera correos electrónicos más antiguos de la misma dirección).

La comprensión clave que me faltaba es que el EveryMessageobjeto no se pasó automáticamente por referencia. Vengo de JavaScript, donde los objetos, matrices y variables se pasan automáticamente por referencia, mientras que en AppleScript uno debe declararlo como un objeto de referencia.

Me he encontrado con un par de problemas más al implementar este script.Esta es la regla que ejecuta mi script.

Esta es la regla que ejecuta mi script. Tenga en cuenta que el comando 'mover' siempre se ejecuta antes que el script, incluso si intento colocarlo después del comando de ejecución del script. Mail.app no ​​parecía ejecutar la secuencia de comandos, y pensé que podría deberse a mover primero el mensaje entrante al buzón de destino y luego ejecutar la secuencia de comandos en el buzón de destino. Dado que la secuencia de comandos compara los mensajes entrantes ( eachMessage in these_messages) con los mensajes en el buzón de correo de destino ( everyMessage), ¿quizás no quedan eachMessageobjetos después del movimiento ejecutado para usar en la secuencia de comandos? Para probar mi teoría, decidí incorporar el comando mover en mi secuencia de comandos, de la siguiente manera:

using terms from application "Mail"
    on perform mail action with messages these_messages for rule this_rule
        tell application "Mail"
            set everyMessage to a reference to messages in mailbox "Charitable appeals" of account "iCloud"
            repeat with eachMessage in these_messages
                delete (everyMessage where ¬
                    the date received is not the (current date) ¬
                    and (its sender is the sender of eachMessage ¬
                    or its reply to is equal to the reply to of eachMessage))
            end repeat
            repeat with eachMessage in these_messages
                move eachMessage to mailbox "Charitable appeals" of account "iCloud"
            end repeat
        end tell
    end perform mail action with messages
end using terms from

Entonces ahora mi regla solo ejecuta mi script en los mensajes entrantes apropiados. El problema ahora es que, si accedo Apply Rulesmanualmente a un mensaje desde el menú de mail.app, el script se ejecuta como se esperaba, pero cuando la regla se ejecuta automáticamente en los mensajes entrantes, mail.app se cuelga, es decir, not respondingtengo que forzar el cierre y reiniciar, a veces primero deshabilitando el script. Pensé que podría deberse a que había más de 100 mensajes en el buzón de destino, pero luego los limpié para tener solo ~ 50 mensajes, y el bloqueo persiste.

¡Agradecería mucho cualquier ayuda, de @CJK o de cualquier persona!