Announcement

Collapse

Forum Guidelines

This forum is for finished source code that is working properly. If you have questions about this or any other source code, please post it in one of the Discussion Forums, not here.
See more
See less

gbTweet - Text Messenger

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • gbTweet - Text Messenger

    Here's my latest application, gbTweet, which allows multiple users to send real-time messages to one another. You could describe it as essentially a one-thread forum..

    I've been using Skype to chat with my daughter recently and have seen significant delays in messages reaching the other end. That made me wonder if I could do something with PowerBASIC that is very lightweight and faster. In my tests so far, this approach has been very responsive.

    Discussion is here.
    Download the EXE, images and source code here.

    Multiple users can chat using gbTweet, but it only supports a single "thread". All user tweets are presented to all other users as a simple listing of tweets. The user name and date are sent with the tweet, as seen in this gbTweet image:

    Click image for larger version  Name:	gbtweet.jpg Views:	1 Size:	32.4 KB ID:	773491

    Users can send a single line tweet of up to 250 characters. Once a tweet is submitted, gbTweet adds it to the online tweet history file, then downloads the current history file for display in the local app

    Users can update the tweet history manually, or have gbTweet update the history automatically on intervals of their choosing (5s, 10s, 30s, 60s).

    gbTweet does not support private conversations, images, nor active links. I can envision adding those in the future but this first release of gbTweet is simply a minimal text messaging utility. The use of a RichEdit control, for example, would be an easy way of supporting activelinks. Private conversations could be handled by submitting a tweet history file name to the PHP script to use, where a group the file name is known only to a certain group of people. For added security, I could add an encryption/decryption function where users would simply share the password to enable them to pass protected information. I'll work more on all of those in the future.

    But, to be clear, there is NO real security associated with gbTweet at this time. You should consider all tweets as available to any interested users.

    Currently, the PHP file and tweet history is located on my own server. If you wanted a messenger for your own group of family, friends or associates you'd simply put the PHP file on your own server and edit the local app INI files values that point to that server, or change the app code itself to include your server information.

    gbTweet requires no password and no registration is required.

    Features:
    • Single thread Chat/Forum/Text Messenger
    • Multiple users
    • Automatic tweet history updates
    • User Name and Date added to user comments
    • Tweet History archived locally
    • Remote clearing of tweet history
    • includes one local PowerBASIC app and one server-side PHP script
    gbTweet consists of a local PowerBASIC application and a serer-side PHP script.

    Here's the PowerBASIC source code.

    Code:
    'Compilable Example:
    #Compiler PBWin 10
    #Compile Exe "gbtweet.exe"
    #Debug Display On
    #Debug Error On
    #Dim All
    %Unicode=1
    #Include "Win32API.inc"
    #Include "WinINET.inc"
    
    #Resource Manifest, 1, "files/xptheme.xml"
    #Resource Icon, logo,   "files/messages.ico"
    #Resource Icon, xhelp,  "files/help.ico"
    #Resource Icon, xreset, "files/reset.ico"
    #Resource Icon, xman, "files/man.ico"
    
    #Resource VersionInfo
    #Resource FileFlags      0
    #Resource FileVersion    1, 0, 0, 0
    #Resource ProductVersion 1, 0, 0, 0
    #Resource StringInfo "0409", "04B0"
    #Resource Version$       "Comments",         "Single Thread Forum"
    #Resource Version$       "CompanyName",      "New Vision Concepts"
    #Resource Version$       "FileDescription",  "Single Thread Forum"
    #Resource Version$       "FileVersion",      "1.0"
    #Resource Version$       "InternalName",     "gbTweet"
    #Resource Version$       "LegalCopyright",   "© by Gary Beene 2018"
    #Resource Version$       "OriginalFilename", "gbTweet.exe"
    #Resource Version$       "LegalTrademarks",  "///"
    #Resource Version$       "ProductName",      "gbTweet"
    #Resource Version$       "ProductVersion",   "1.0"
    
    $ver = "  v1.0"
    
    Enum Equates Singular
       IDT_Submit = 500
       IDT_Refresh
       IDT_AutoRefresh
       IDT_RefreshRate
       IDT_Name
       IDT_Help
    
       ID_Timer
    
       IDC_Toolbar
       IDC_Tweet
       IDC_Tweets
       IDC_Label
       IDC_Statusbar
    
       IDM_Cut
       IDM_Paste
       IDM_Copy
       IDM_Delete
       IDM_Clear
    End Enum
    
    Global hDlg, hToolbar, hTweets, hTweet, hContext, ghHook As Dword
    Global MyTweet, History As String
    Global AutoRefresh, RefreshRate, Seconds, OldProc, Hook As Long
    
    Global ServerTweetsPath, ServerPath, PHPPath, UserName As WStringZ * %Max_Path
    
    Function PBMain() As Long
       Dialog Default Font "Tahoma", 12, 1
       Dialog New Pixels, 0, "gbTweet" + $ver,300,300,500,350, %WS_OverlappedWindow Or %WS_ClipChildren, To hDlg
       Dialog Set Icon hDlg, "logo"
       Control Add TextBox, hDlg, %IDC_Tweet,"",0,0,500,100, %ES_AutoHScroll Or %WS_HScroll Or %WS_TabStop Or %WS_ClipSiblings, %WS_Ex_ClientEdge
       Control Handle hDlg, %IDC_Tweet To hTweet
       Control Add TextBox, hDlg, %IDC_Tweets,"", 0,100,500,500,  %ES_MultiLine Or %WS_VScroll  Or %ES_AutoVScroll Or %ES_WantReturn Or %WS_TabStop Or %WS_ClipSiblings, %WS_Ex_ClientEdge
       Control Handle hDlg, %IDC_Tweets To hTweets
       Control Add Label, hDlg, %IDC_Label, "", 0,0,500,4
       Control Set Color hDlg, %IDC_Label, %rgb_Maroon, %rgb_Maroon
       Control Add Statusbar, hDlg, %IDC_StatusBar, "Welcome to gbTweet",0,0,0,0
       Statusbar Set Parts hDlg, %IDC_Statusbar, 350,75,75
       Dialog Show Modal hDlg Call DlgProc
    End Function
    
    CallBack Function DlgProc() As Long
       Local temp$, iResult,w,h As Long, P As ToolTipText Ptr, nmtb As TBNotify Ptr, rc As Rect
    
       Select Case Cb.Msg
          Case %WM_InitDialog
             OldProc = SetWindowLong(GetDlgItem(hDlg, %IDC_Tweet), %GWL_WndProc, CodePtr(NewProc))
             OldProc = SetWindowLong(GetDlgItem(hDlg, %IDC_Tweets), %GWL_WndProc, CodePtr(NewProc))
             Settings_INI "get"
             CreateToolbar
             GetLocalTweet
             GetServerTweets  'GetLocalTweets
             SetFocusOnTweet
             UpdateStatusBar
             CreateCustomPopupMenu
             If AutoRefresh Then SetTimer hDlg, %ID_Timer, 1000, ByVal %Null
    
          Case %WM_Destroy :
             SaveLocalTweet
             SaveLocalTweets
             Settings_INI "save"
             SetWindowLong GetDlgItem(hDlg, %IDC_Tweet), %GWL_WNDPROC, OldProc   'un-subclass
             SetWindowLong GetDlgItem(hDlg, %IDC_Tweets), %GWL_WNDPROC, OldProc   'un-subclass
    
          Case %WM_Timer
             If AutoRefresh Then
                Incr Seconds
                If Seconds Mod RefreshRate = 0 Then
                   GetServerTweets
                   Statusbar Set Text hDlg, %IDC_StatusBar, 1, 0, " AutoRefreshed at " + Time$
                End If
             End If
    
          Case %WM_Command
             Select Case Cb.Ctl
                Case %IDT_Submit, %IdOk
                   SendTweet
                   GetServerTweets
                   Statusbar Set Text hDlg, %IDC_StatusBar, 1,0, IIf$(InStr(MyTweet,"zzz..."),"Tweet history cleared!", "Tweet sent ..." + Date$ + $Spc + Time$)
                   SetFocusOnTweet
    
                Case %IDT_Refresh       : GetServerTweets      : SetFocusOnTweet
                Case %IDT_Help          : DisplayHelpMessage   : SetFocusOnTweet
                Case %IDT_AutoRefresh   : ToggleAutoRefresh    : SetFocusOnTweet : UpdateStatusBar
                Case %IDT_RefreshRate   : ToggleRefreshRate    : SetFocusOnTweet : UpdateStatusBar
                Case %IDT_Name          : If Hook=0 Then Hook = 1 : EnterUserName : Hook = 0
    
                Case %IDM_Cut     : SendMessage GetFocus, %WM_CUT, 0, 0
                Case %IDM_Copy    : SendMessage GetFocus, %WM_COPY, 0, 0
                Case %IDM_Paste   : SendMessage GetFocus, %WM_PASTE, 0, 0
                Case %IDM_Delete  : SendMessage GetFocus, %WM_CLEAR, 0, 0
                Case %IDM_Clear   : SendMessage GetFocus, %WM_SetText, 0, StrPtr(temp$)
    
             End Select
    
          Case %WM_Notify
               Select Case Cb.NmId
                   Case %IDT_Submit To %IDT_Help
                      Select Case Cb.NmCode
                         Case %TTN_GetDispInfo
                             P = Cb.LParam
                             @P.@lpszText = SetToolTips(Cb.NmId)
                   End Select
               End Select
    
          Case %WM_Help
    
          Case %WM_Size
             Dialog Get Client hDlg To w,h
             Control Set Loc hDlg, %IDC_Tweet, 0,70
             Control Set Size hDlg, %IDC_Tweet, w,50
             Control Set Loc hDlg, %IDC_Label, 20,130
             Control Set Size hDlg, %IDC_Label, w-40,6
             Control Set Loc hDlg, %IDC_Tweets, 0,150
             Control Set Size hDlg, %IDC_Tweets, w,h-175
             Control ReDraw hDlg, %IDC_Label
       End Select
    End Function
    
    Sub Settings_INI(Task$)
       Local x As Long, y As Long
       Local xResult, yResult, temp, INIFileName As WStringZ*%Max_Path
    
       INIFileName = Exe.Path$ + "gbtweet.ini"
    
       If Task$ = "get" Then
          'get dialog top/left from INI file and use to set Dialog location
          Getprivateprofilestring "All", "Left", "300", xResult, %Max_Path, INIFileName
          Getprivateprofilestring "All", "Top", "300", yResult, %Max_Path, INIFileName
          Dialog Set Loc hDlg, Val(xResult), Val(yResult)   'left/top
    
          'get dialog width/height from INI file and use to set Dialog size
          GetPrivateProfileString "All", "Width", "500", xResult, %Max_Path, INIFileName
          GetPrivateProfileString "All", "Height", "600", yResult, %Max_Path, INIFileName
          Dialog Set Size hDlg,Val(xResult), Val(yResult)   'width/height
    
          'get value for string variables
          Getprivateprofilestring "All", "ServerTweetsPath", "http://www.garybeene.com/tweet/tweets.txt", ServerTweetsPath, %Max_Path, INIFileName
          Getprivateprofilestring "All", "ServerPath", "garybeene.com", ServerPath, %Max_Path, INIFileName
          Getprivateprofilestring "All", "PHPPath",  "/tweet/tweet.php", PHPPath, %Max_Path, INIFileName
          Getprivateprofilestring "All", "UserName",  "UserName", UserName, %Max_Path, INIFileName
    
          'get value for numeric variables
          Getprivateprofilestring "All", "RefreshRate", "60", temp, %Max_Path, INIFileName:  RefreshRate = Val(temp)
          Getprivateprofilestring "All", "AutoRefresh", "0", temp, %Max_Path, INIFileName:  AutoRefresh = Val(temp)
       End If
    
       If Task$ = "save" Then
          'save dialog size/location unless minimized or maximized
          If IsFalse(IsIconic(hDlg) Or IsZoomed(hDlg)) Then
             Dialog Get Loc hDlg To x,y
             WritePrivateProfileString "All", "Left", Str$(x), INIFileName
             WritePrivateProfileString "All", "Top", Str$(y), INIFileName
             Dialog Get Size hDlg To x,y
             WritePrivateProfileString "All", "Width", Str$(x), INIFileName
             WritePrivateProfileString "All", "Height", Str$(y), INIFileName
          End If
          'save string variables
          WritePrivateProfileString "All", "ServerTweetsPath",ServerTweetsPath, INIFileName
          WritePrivateProfileString "All", "ServerPath",ServerPath, INIFileName
          WritePrivateProfileString "All", "PHPPath",PHPPath, INIFileName
          WritePrivateProfileString "All", "UserName",UserName, INIFileName
    
          'save numeric variables
          WritePrivateProfileString "All", "RefreshTime", (Str$(RefreshRate)), INIFileName
          WritePrivateProfileString "All", "AutoRefresh", (Str$(AutoRefresh)), INIFileName
       End If
    End Sub
    
    Sub CreateToolbar
       Local hImageList As Dword
        ImageList New Icon 24,24,24,10 To hImageList
        ImageList Add Icon hImageList, "logo"   '1
        ImageList Add Icon hImageList, "xhelp"  '2
        ImageList Add Icon hImageList, "xreset" '3
        ImageList Add Icon hImageList, "xman" '4
    
        Control Add Toolbar, hDlg, %IDC_Toolbar,"", 0,0,0,0, %TbStyle_Tooltips Or %TbStyle_Flat Or %WS_Child
        Control Handle hDlg, %IDC_Toolbar To hToolbar
    
        Toolbar Set ImageList hDlg, %IDC_Toolbar, hImageList, 0     'default imagelist
    
        Toolbar Add Button  hDlg, %IDC_Toolbar, 1, %IDT_Submit,  %TbStyle_Button, "Submit"
        Toolbar Add Separator hDlg, %IDC_Toolbar,25
        Toolbar Add Button  hDlg, %IDC_Toolbar, 3, %IDT_Refresh,  %TbStyle_Button, "Refresh"
        Toolbar Add Button  hDlg, %IDC_Toolbar, 3, %IDT_AutoRefresh,  %TbStyle_Button, "Auto"
        Toolbar Add Button  hDlg, %IDC_Toolbar, 3, %IDT_RefreshRate,  %TbStyle_Button, "Rate"
        Toolbar Add Separator hDlg, %IDC_Toolbar,25
        Toolbar Add Button  hDlg, %IDC_Toolbar, 4, %IDT_Name,  %TbStyle_Button, "Name"
        Toolbar Add Separator hDlg, %IDC_Toolbar,25
        Toolbar Add Button  hDlg, %IDC_Toolbar, 2, %IDT_Help,  %TbStyle_Button, "Help"
    End Sub
    
    Function SetToolTips(cID As Long) As String
       Select Case cID          'or this:   @P.hdr.idFrom
          Case %IDT_Submit        : Function = "Submit Tweet"
          Case %IDT_Help          : Function = "Open Help"
          Case %IDT_AutoRefresh   : Function = "Auto Refesh Tweet History"
          Case %IDT_RefreshRate   : Function = "Refresh Tweet History Every" + Str$(RefreshRate) + "s"
          Case %IDT_Refresh       : Function = "Refresh Tweet History Now"
          Case %IDT_Name          : Function = "User Name: " + UserName
       End Select
    End Function
    
    Sub SendTweet
       Local sHead$, sResponse$, iStart, iEnd As Long
       Control Get Text hDlg, %IDC_Tweet To MyTweet
       sResponse$ = WebPost((ServerPath), (PHPPath), "tweet=" + UserName + " - " + MyTweet)
       iStart = InStr(sResponse$,"<body>") + 6
       iEnd   = InStr(-1,sResponse$,"</body>") - 1
       Control Set Text hDlg, %IDC_Tweets, Mid$(sResponse$,iStart To iEnd)   'put PHP response in Tweets textbox
    End Sub
    
    Function WebPost(sServer$, sPHPCom$, sData$) As String
       Local sTemp$, recBuffer$   'returns body of generated web page
       Tcp Open Port 80 At sServer$ As #1 TimeOut 1000  'Connect to the server
       Tcp Print #1, "POST " + sPHPCom$ + " HTTP/1.0"
       Tcp Print #1, "Accept: */*"
       Tcp Print #1, "Accept-Language: en-us"
       Tcp Print #1, "Host: " + sServer$
       Tcp Print #1, "User-Agent: web-poster"
       Tcp Print #1, "Content-Type: application/x-www-form-urlencoded"
       Tcp Print #1, "Content-Length: " + Format$(Len(sData$))
       Tcp Print #1, ""
       Tcp Print #1, sData$
       Sleep 80
       Do
        Tcp Recv #1, 1024, recBuffer$
        stemp$ = stemp$ & recBuffer$
       Loop Until (IsFalse Len(recBuffer$) And IsTrue Eof(1)) Or IsTrue Err
       Tcp Close #1
       WebPost = LTrim$(Remain$(sTemp$, $CrLf + $CrLf), $CrLf)
    End Function
    
    Sub SaveLocalTweet
       Local temp$
       Control Get Text hDlg, %IDC_Tweet To temp$
       Open "tweet.txt" For Output As #1
       Print #1, temp$;
       Close #1
    End Sub
    
    Sub SaveLocalTweets
       Local temp$
       Control Get Text hDlg, %IDC_Tweets To temp$
       Open "tweets.txt" For Output As #1
       Print #1, temp$;
       Close #1
    End Sub
    
    Sub GetLocalTweet
       Local temp$
       Open "tweet.txt" For Binary As #1
       Get$ #1, Lof(1), temp$
       Close #1
       Control Set Text hDlg, %IDC_Tweet, temp$
    End Sub
    
    Sub GetLocalTweets
       Local temp$
       Open "tweets.txt" For Binary As #1
       Get$ #1, Lof(1), temp$
       Close #1
       Control Set Text hDlg, %IDC_Tweets, temp$
    End Sub
    
    Sub GetServerTweets
       Local URLPath, LocalPath As WStringZ*%Max_Path, temp$
       URLPath = ServerTweetsPath  'location of tweets.txt on server
       LocalPath = Exe.Path$ + "tweets.txt"
       DeleteURLCacheEntry(URLPath)     '1 = success  clear the cache
       If URLDownloadToFile (Nothing, URLPath, LocalPath, 0, Nothing) = 0 Then
          Open Exe.Path$ + "tweets.txt" For Binary As #1
          Get$ #1, Lof(1), temp$
          Close #1
          Control Set Text hDlg, %IDC_Tweets, Trim$(temp$, Any $CrLf)
          ScrollToBottom
          Statusbar Set Text hDlg, %IDC_StatusBar, 1,0, "Tweet history refreshed!"
       Else
          Statusbar Set Text hDlg, %IDC_StatusBar, 1,0, "Tweet history not refreshed!"
       End If
    End Sub
    
    Sub ScrollToBottom
      Local tLen As Long
      Control Send hDlg, %IDC_Tweets, %WM_GetTextLength, 0, 0 To tLen
      Control Send hDlg, %IDC_Tweets, %EM_SETSEL, tLen, tLen
      Control Send hDlg, %IDC_Tweets, %EM_SCROLLCARET, 0, 0
    End Sub
    
    Sub ToggleAutoRefresh
       AutoRefresh Xor= 1
       If AutoRefresh Then
          SetTimer hDlg, %ID_Timer, 1000, ByVal %Null
       Else
          KillTimer hDlg, %ID_Timer
       End If
    End Sub
    
    Sub UpdateStatusBar
       Statusbar Set Text hDlg, %IDC_StatusBar, 2, 0, IIf$(AutoRefresh," AutoON", " AutoOFF")
       Statusbar Set Text hDlg, %IDC_StatusBar, 3, 0, Str$(RefreshRate) + "s"
    End Sub
    
    Sub ToggleRefreshRate
       Select Case RefreshRate
          Case 5    : RefreshRate = 10 : Seconds = 10
          Case 10   : RefreshRate = 30 : Seconds = 30
          Case 30   : RefreshRate = 60 : Seconds = 60
          Case Else : RefreshRate = 5  : Seconds = 5
       End Select
       If AutoRefresh Then
          KillTimer hDlg, %ID_Timer
          SetTimer hDlg, %ID_Timer, 1000, ByVal %Null
       End If
    End Sub
    
    Sub SetFocusOnTweet
       Control Set Focus hDlg, %IDC_Tweet
       Control Post hDlg, %IDC_Tweet, %EM_SETSEL, 0, -1
    End Sub
    
    Sub DisplayHelpMessage
       Local temp$
       temp$  = "gbTweet " + $ver + $CrLf + _
       "Submit or Enter - sends tweet" + $CrLf + _
       "Refresh - loads latest server tweet history" + $CrLf + _
       "Auto - toggles automatic refresh" + $CrLf + _
       "Rate - cycles through 5s, 10s, 30s, 60s refresh rates"
       MsgBox temp$, + %MB_Ok And %MB_IconInformation, "gbTweet"
    End Sub
    
    Sub CreateCustomPopUpMenu
       Menu New PopUp To hContext
       Menu Add String, hContext, "Copy",   %IDM_Copy,  %MF_Enabled
       Menu Add String, hContext, "Cut",    %IDM_Cut,  %MF_Enabled
       Menu Add String, hContext, "Paste",  %IDM_Paste,  %MF_Enabled
       Menu Add String, hContext, "Delete", %IDM_Delete,  %MF_Enabled
       Menu Add String, hContext, "Clear",  %IDM_Clear,  %MF_Enabled
    End Sub
    
    Function NewProc(ByVal hWnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
       Local x,y As Long
       Select Case Msg
          Case %WM_ContextMenu
             SetFocus wParam
             x = Lo(Integer,lParam) : y = Hi(Integer, lParam)    'WM_ContextMenu returns xy coordinates of mouse
             TrackPopupMenu hContext, %TPM_LeftAlign, x, y, 0, hDlg, ByVal 0   'put context menu where mouse is
             Function = 0 : Exit Function
       End Select
       Function = CallWindowProc(OldProc, hWnd, Msg, wParam, lParam)
    End Function
    
    Sub EnterUserName
       Local temp$, x, y As Long
       Dialog Get Loc hDlg To x,y
       ghHook = SetWindowsHookEx(%WH_CBT, CodePtr(InputBoxProc), GetModuleHandle(""), GetCurrentThreadId)
       temp$ = InputBox$("Enter your name:", "Enter User Name:", UserName,x+100,y+150)
       If Len(temp$) Then UserName = temp$
    End Sub
    
    Function InputBoxProc(ByVal nCode As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
       Local szTemp As WStringZ * %Max_Path, cw As CBT_CREATEWND Ptr, cst As CREATESTRUCT Ptr, iStyle As Long
       Function = CallNextHookEx(ByVal ghHook, ByVal nCode, ByVal wParam, ByVal lParam)
       If nCode < 0 Then Exit Function
       If nCode = %HCBT_ACTIVATE Then UnhookWindowsHookEx ghHook
       If nCode = %HCBT_CREATEWND Then
          cw = lParam : cst = @cw.lpcs                ' get a pointer to the CREATESTRUCT struct
          GetClassName wParam, szTemp, %Max_Path      ' for each window / control as it is created
          If UCase$(szTemp) = "BUTTON" Then
             Select Case @cst.hMenu
                Case %IdOk   :   @cst.x=20:@cst.y=40:@cst.cx=60
                Case %IdCancel : @cst.x=110:@cst.y=40:@cst.cx=60
             End Select
          End If
          If UCase$(szTemp) = "STATIC" Then @cst.x = 400  'prompt moved out of the way
          If UCase$(szTemp) = "EDIT"   Then
             @cst.x = 10   : @cst.y = 10 : @cst.cx = 175 : @cst.cy = 25
    '         iStyle = GetWindowLong(wParam,%GWL_Style)
    '         SetWindowLong wparam, %GWL_Style, iStyle Or %ES_Password
          End If
          If UCase$(szTemp) = "#32770" Then @cst.cx = 200 : @cst.cy = 100  'dialog
       End If
    End Function
    And here is the PHP code:

    Code:
    <!DOCTYPE html><html><body>
    <?php
    
    //get tweet from user
    $post = $_POST["tweet"];
    
    //set clear history code
    $kill = "fffjjj";
    
    $pos = strpos($post,$kill);
    If ($pos === false) {
     //append new tweet to tweet file
     $tweetfile = fopen("tweets.txt","a");
    } else {
     //overwrite - clear the tweets.txt file
     $tweetfile = fopen("tweets.txt","w");
     $post = "Welcome to gbTweet";
    }
     $datetime = "\r\n----------  ".date("d-m-Y  h-i-s")."\r\n";
     fwrite($tweetfile, $datetime);
    
    //security processing
    $safepost = substr($post, 0, 250);
    
    //save
     fwrite($tweetfile, $safepost);
     fclose($tweetfile);
    
    //read the tweet file into a variable and return it
    $tweets = file_get_contents("tweets.txt");
    echo $tweets;
    
    ?>
    </body></html>
    Attached Files
Working...
X