Showing posts with label Programming. Show all posts
Showing posts with label Programming. Show all posts

Friday, July 13, 2012

Google App Scripts

In the last few days I discovered that it is possible to script Google apps using just plain Javascript. For some time I wanted to automate a few tasks that I do in Gmail and never found an appropriate way to do it. What makes this even more exciting is that the scripting environment is all integrated with the Google Drive environment. That is, you can create and manage your scripting projects just as easily as managing your sdpreadsheets and documents.

So I wouldn't waste time explaining the details of the scripting environment since there are some very good documentation and tutorials already. But I will explain how I started using this feature.

The Backgroound

We are running a website with the usual email feedback forms. Unfortunately because of the way the email system and the website's domain name is set up I cannot automate sending the emails directly to the corporate email addresses. I suspect that an experienced system administrator may be able to sort out the details quite easily but that is not me. So instead I send all feedback to a Gmail account from where it is then distributed to the appropriate addresses. Up to now the distribution was done manually. Wouldn't it be nice, I say to myself frequently, if I could write a script that simply forwarded these messages on automatically?

The Solution

I started by creating a new script in Google Drive.





Then I created a function to search the inbox and parse the emails and forward the relevant ones. The thing is, the email address of the department in the organization is actaully in the body of the email. This was originally done just as a summary of the entire transaction will be recorded in the email itself. So the function has to parse the body for the email address.

function forwardWebsiteMail(){  
  var address = "The address of emails from the website";
  var d = new Date();   //todays date
  var msg = "Starting send function at: " + d.toLocaleTimeString();
  if(GmailApp.getInboxUnreadCount() > 0){ //are there any unread messages?
   
    var threads = GmailApp.search('is:unread from:(address)'); //search for all unread messages from the website
    var msgcount = 0;
    var threadcount = threads.length;
 
    for(var i = 0;i < threads.length; i++ ){     //loop through the threads
     
        var messages = threads[i].getMessages();
        for(var j = 0; j < messages.length;j++){  //loop through messages in this thread
          var body = messages[j].getBody();     //parse the body of the mail.    
          var p = body.indexOf("@");
          var found = false;
          var n = p-1;
          while(!found){   //find the start of the address
            if(body.charAt(n) == " "){
              found = true;
            }else{
              n = n - 1;
            }
          }
         
          found = false;
          var m = p + 1;
          while(!found){   //find the end of the address
            if(body.charAt(m) == "<" ){
              found = true;
            }else{
              m = m + 1;
            }
          }
         
          var faddress = body.substring(n + 1, m); //now get the address         
          messages[j].forward(faddress, {noReply:true});  //forward
          messages[j].markRead(); //mark the message as read        
          msgcount = msgcount + 1; //keep track of the total number of messages forwarded
        }
      var label = GmailApp.getUserLabelByName("Work/AutoSent");//add a special label to the thread
      threads[i].addLabel(label);    
    }
    msg = msg + "\n" + msgcount + " messages sent";
    msg = msg + "\nFrom " + threadcount + " threads";
  }else{
    msg = msg + "\nNo Unread Messages";
  }
  //finally notify myself that the script ran
  GmailApp.sendEmail("my email address", "Auto Email Script Report " + d.toLocaleTimeString(), msg);
 
}

You can see from the above function that I am using the GmailApp object to interact with Gmail, the GmailThread and GmailMessage objects to interact with the Threads and Messages respectively.

Automating the Script

Finally I need a way to run this script without interaction from me. There are two possibilities: Time Driven triggers or Simple triggers. Simple Triggers respond to events such as onOpen and Time Driven Triggers can be scheduled to run at periodic intervals.
The Script environment has an option to manage triggers.



It is then a simple matter to set an interval for your function to run. I am runnimng my function once every hour.

Saturday, March 27, 2010

Mastermind in SML

Functional languages are making a comeback. For a while they were in the exclusive domain of academia but recently there has been increased interest in functional programming in the wider industry. I will leave it up to WikiPedia to explain if you never heard of this before.

Here is a simple program written in SML that implements a Mastermind game.

(*
This program implements a mastermind game.
The computer is the board master and the user
has to make guesses until...

The tokens "g", "r", "y", "b" are used to represent

green, red, yellow and blue respectively.

Author: Vincent Ramdhanie
Date: 11 February 2010
*)


(*Start by opening the TextIO library for input and output of text data.*)
open TextIO;

(*Next we need a random number generator*)
val ran = Random.rand(1, 20);
fun random() = Random.randRange(1, 4) ran;

(* member returns true if c is a member of the list, false otherwise. Of course there is a built in member function. But we are trying to learn the language here are we not?*)

fun member(c, []) = false |
    member(c, h::t) = c = h orelse member(c, t);
(*inPosition returns true is c is in the ith position ina given list*)
fun inPosition(c, i, []) = false |
    inPosition(c, 0, h::_) = c = h |
    inPosition(c, i, _::t) = inPosition(c, i - 1, t);


(*We need a datatype to track character correctness*)
datatype correct = A | B | C;


(*This function takes a character and a position and returns a correct value*)
fun charCorrect(c, i, L) =
    if member(c, L) then
        if inPosition(c, i, L) then
            A
        else
            B
    else
        C;

(*Remove all newLine characters from a char list*)
fun removeNewLine([]) = [] |
    removeNewLine(#"\n"::_) = [] |
    removeNewLine(h::t) = h::removeNewLine(t);

(*Remove newline characters from a string *)
fun trimString(s) = implode(removeNewLine(explode(s)));

(*Ensure that only the characters #"g", #"r", #"b" and #"y" are in a string*)
local val chars = [#"g", #"r", #"b", #"y"] in
fun limitChars([]) = true |
    limitChars(h::t) = member(h, chars) andalso limitChars(t)
end;

fun limitString(s) = limitChars(explode(s));

(* Generate Random characters*)
fun randomChar() =
    let
        val r = random()
    in
        case r of
            1 => #"g" |
            2 => #"b" |
            3 => #"r" |
            4 => #"y"
    end;

(*Generate just n characters*)
fun nRandomChars(0) = [] |
    nRandomChars(n) = randomChar()::nRandomChars(n - 1);

(*Create a string of 4 characters*)
fun randomCharString() = implode(nRandomChars(4));

(*Some user interaction functions*)

(*This function gets an input line from the user*)
fun inLine() = valOf(inputLine(stdIn));

(*Prompt the user for input*)
fun prompt(msg) = (print(msg ^ "\r\n");inLine())

(*The Yes No datatype*)
datatype answer = Yes | No;

(*The Valid Invalid datatype*)
datatype valids = Valid | Invalid;

(*Prompt the user for a Yes No value*)
fun yesNoPrompt(msg) =
    let
        val ans = prompt(msg)
    in
        if ans = "Yes\n" then
            Yes
        else
            No
    end;

(* Get a cleaned up guess*)
fun guessPrompt() =
    let
        val guess = prompt("Please enter 4 character guess using the characters g, r, b and y")
    in
        trimString(guess)
    end;


(* Validate Guess*)
fun getValidGuess() =
    let
        val inp = guessPrompt()
        val lim = limitString(inp)
    in
        if (size(inp)  = 4) andalso lim then
            inp
        else
            (print("Sorry, That input is invalid. Please try again.\n\n");getValidGuess())
    end;

(*  Print a report*)
fun printResult([]) = print("_______________________\n") |
    printResult(h::t) =
    if h = A then
        (print("Correct Position\n");printResult(t))
    else
        if h = B then
            (print("Incorrect Position\n");printResult(t))
        else
            (print("Incorrect Token\n");printResult(t))

(*Determine the quality of a guess*)
fun processGuessAux(original, _, []) = [] |
    processGuessAux(original,i, h::t) =  charCorrect(h, i, original)::processGuessAux(original,i + 1, t);

fun processGuess(original, guess) =
    if original = guess then
        true
    else
        (printResult(processGuessAux(explode(original), 0, explode(guess)));false)
   

(*A run of the Game*)
fun gameRun(original) =
    let
        val guess = getValidGuess()
                val result = processGuess(original, guess)
    in
        if result then
            print("Congratulations. You Win!")
        else
            gameRun(original)
    end;

(*Game Control*)
fun playAgain() =
    let
        val ans = yesNoPrompt("Do you want to play again? (Yes/No):\n")
    in
        if ans = Yes then
            play()
        else
            print("Thank You for playing.")
    end
and
   play() =
    let
        val original = randomCharString()
    in
        (gameRun(original);playAgain())
    end;