![]() |
Edit |
![]() |
|
![]() |
Recent Changes |
![]() |
Subscriptions |
![]() |
Lost and Found |
![]() |
Find References |
![]() |
Rename |
| Search |
![]() |
List all versions |
Lightweight WPF Twitter client sample F# script
Change the username and password in the code, then simply run with F# interactive.
The client lets you both see your friend's timeline and post tweets, all in under 200 lines of F#!
Advertising degree | hr degree | Online International business degree | Online management degree | Marketing degree
// fsweet - WPF Twitter client F# script by Phillip Trelford 2009
#light
#r "PresentationCore.dll"
#r "PresentationFramework.dll"
#r "WindowsBase.dll"
open System
open System.Diagnostics
open System.IO
open System.Net
open System.Text
open System.Windows
open System.Windows.Controls
open System.Windows.Documents
open System.Xml
// Enter your username and password here
let username, password = "<your username>", "<your password>"
/// Gets web response as an XmlDocument
let GetXmlWebResponse (request:WebRequest) =
try
use response = request.GetResponse()
use stream = response.GetResponseStream()
let doc = XmlDocument() in doc.Load(stream)
Some(doc)
with e -> e.ToString() |> printf "Error: %s"; None
/// Gets Xml response from Web Url
let Get (url:string) =
let request = WebRequest.Create(url)
request.Credentials <- NetworkCredential(username, password)
GetXmlWebResponse request
/// Posts data to Web Url
let Post (url:string) user (data:string) =
let request = WebRequest.Create(url) :?> HttpWebRequest
request.Method <- "POST"
request.ServicePoint.Expect100Continue <- false
request.Headers.Add("Authorization", "Basic " + user)
let bytes = System.Text.Encoding.ASCII.GetBytes(data);
request.ContentType <- "application/x-www-form-urlencoded"
request.ContentLength <- int64 bytes.Length
request.GetRequestStream().Write(bytes, 0, bytes.Length)
GetXmlWebResponse request
/// Posts user tweet
let Tweet tweet =
let user = Convert.ToBase64String(Encoding.UTF8.GetBytes(username+":"+password));
"status=" + tweet |> Post "http://twitter.com/statuses/update.xml" user
/// Gets user time line
let GetUserTimeLine () =
sprintf @"http://twitter.com/statuses/user_timeline/%s.xml" username |> Get
/// Gets friends time line
let GetFriendsTimeLine () =
"http://twitter.com/statuses/friends_timeline.xml" |> Get
let NodeText (node:XmlNode) child = node.[child].InnerText
let Date s = XmlConvert.ToDateTime(s, "ddd MMM dd HH:mm:ss zzzzz yyyy")
/// Creates user tuple
let CreateUser (user:XmlNode) =
let Text = NodeText user // Curry NodeText function
(Text "name", Text "screen_name", Text "profile_image_url")
/// Creates status tuple
let CreateStatus (status:XmlNode) =
let Text = NodeText status // Curry NodeText function
(Text "created_at" |> Date, Text "text", CreateUser status.["user"])
/// Gets statuses
let GetStatuses () =
match GetFriendsTimeLine () (*GetUserTimeLine()*) with
| Some doc ->
seq { for status in doc.DocumentElement.SelectNodes("status") do
yield CreateStatus status }
| None -> Seq.empty
// Table layout panel type extends Grid for easier use in code
type TableLayout (columnDefinitions:string seq,rowDefinitions:string seq) =
inherit Grid ()
/// Converts grid length string to GridLength instance
let ParseGridLength (s:string) =
match s.Length, s.EndsWith("*") with
| 0, _ -> GridLength()
| n, false -> GridLength(Double.Parse(s))
| 1, true -> GridLength(1.0, GridUnitType.Star)
| n, true -> GridLength(Double.Parse(s.Substring(0, n-1)), GridUnitType.Star)
do columnDefinitions
|> Seq.map (fun text -> ColumnDefinition(Width=ParseGridLength text))
|> Seq.iter base.ColumnDefinitions.Add
do rowDefinitions
|> Seq.map (fun text -> RowDefinition(Height=ParseGridLength text))
|> Seq.iter base.RowDefinitions.Add
/// Adds item to layout, automatically setting grid column and row values
member this.AddItem (item:#UIElement) =
let span = match this.ColumnDefinitions.Count with | 0 -> 1 | n -> n
let index = this.Children.Count
item |> this.Children.Add |> ignore
Grid.SetColumn(item, index % span)
Grid.SetRow(item, index / span)
/// Creates bitmap image
let CreateBitmap uri =
let img = Media.Imaging.BitmapImage()
img.BeginInit(); img.UriSource <- uri; img.EndInit()
img
/// Annotates hyperlinks converting text to an Inline array
let AnnotateLinks (text:string) =
let rec GetUrlPositions (n:int) (acc) =
match text.IndexOf("http", n) with
| -1 -> acc
| n -> n::(GetUrlPositions (n+1) acc)
GetUrlPositions 0 []
|> Seq.map (fun first ->
first,
match text.IndexOfAny([|' ';'\t';'\r';'\n'|], first) with
| -1 -> text.Length
| n -> n
)
|> Seq.fold (fun (start,lines) (first, last) ->
let leadin = text.Substring(start, first - start)
let url = text.Substring(first, last - first)
let uri = Uri(url, UriKind.Absolute)
let link = Hyperlink(Run(url), NavigateUri=uri)
link.Click.Add (fun _ -> Process.Start(url) |> ignore )
(last, (link :> Inline) :: (Run(leadin) :> Inline) :: lines)
) (0, [])
|> (fun (start, lines) -> (Run(text.Substring(start)) :> Inline) :: lines)
|> Seq.to_array
|> Array.rev
/// Renders statuses in specified control
let RenderStatuses (control:#ItemsControl) statuses =
control.Items.Clear()
statuses
|> Seq.iter (fun (createdAt, text, user) ->
let name, screen, url = user
let across = new TableLayout(["40";"*"], [])
let bitmap = Uri(url, UriKind.Absolute) |> CreateBitmap
Image(Source=bitmap) |> across.AddItem
let down = new TableLayout([], ["1*";"2*";"1*"])
Label(Content=screen, FontWeight=FontWeights.Bold, ToolTip=name) |> down.AddItem
let block = TextBlock(TextWrapping=TextWrapping.Wrap)
AnnotateLinks text |> block.Inlines.AddRange
down.AddItem block
Label(Content=createdAt, FontStyle=FontStyles.Italic) |> down.AddItem
across.AddItem down
control.Items.Add(across) |> ignore
)
[<STAThread>]
do let statusBox = new TextBox(TextWrapping=TextWrapping.Wrap)
let statusItems = new ListBox(Margin=Thickness(4.0))
ScrollViewer.SetHorizontalScrollBarVisibility(statusItems,ScrollBarVisibility.Disabled)
let characterCounter = new Label(FontSize=20.0,FontFamily=Media.FontFamily("Georgia"))
let updateButton = new Button(Content="Update", Width=80.0, Height=24.0, IsEnabled=false)
let SetCharacterCount () =
let count = 140 - statusBox.Text.Length
characterCounter.Content <- count
characterCounter.Foreground <-
match count with
| n when n >= 0 -> Media.SolidColorBrush(Media.Colors.Gray)
| _ -> Media.SolidColorBrush(Media.Colors.Red)
SetCharacterCount ()
statusBox.TextChanged.Add (fun _ ->
SetCharacterCount ()
updateButton.IsEnabled <- statusBox.Text.Length > 0
)
updateButton.Click.Add (fun _ ->
Tweet statusBox.Text |> ignore
GetStatuses () |> RenderStatuses statusItems
statusBox.Clear()
)
let CreateStatusLayout () =
let header = TableLayout(["*";"80"],[])
Label(Content="What are you doing?", FontSize=16.0) |> header.AddItem
characterCounter |> header.AddItem
let footer = TableLayout(["*";"80"],[])
Label() |> footer.AddItem
updateButton |> footer.AddItem
let whatLayout = TableLayout([], ["1*";"2*";"1*"])
whatLayout.Margin <- Thickness(4.0)
whatLayout.AddItem header
whatLayout.AddItem statusBox
whatLayout.AddItem footer
whatLayout
let windowLayout = TableLayout([], ["160";"*"])
CreateStatusLayout () |> windowLayout.AddItem
statusItems |> windowLayout.AddItem
let window = new Window(Title="fsweet", Width=320.0, Height=400.0)
//window.Icon <- Media.Imaging.BitmapFrame.Create(Uri(@"twitter_icon_16x16.bmp", UriKind.Relative))
window.Content <- windowLayout
window.Loaded.Add (fun _ -> GetStatuses () |> RenderStatuses statusItems)
(new Application()).Run(window) |> ignore
![]() |
| This site supports the new NoFollow anti-spam initiative. |
Recent Topics