Sistem Presensi Kuliah dengan Twitter (Twitter sebagai SMS Gateway)

10 February 2011 at 11:13 | Posted in Uncategorized | 3 Comments

Semester  ini saya mulai menerapkan aturan kehadiran sebagai syarat ujian.  Sistem yang ada saat ini adalah berbasis tandatangan, dan saya malas merekapnya menjelang  ujian (malas itu adalah ibu-nya dari penemuan hehe).  Setelah mempertimbangkan beberapa hal, akhirnya saya menjadikan Twitter sebagai media. Skenarionya adalah sbb:

  1. Mahasiswa mendaftarkan account, nim dan nama, dengan mengirimkan tweet dengan me-mention account khusus yang telah disiapkan.   Misalnya @sayahadir reg 04001 Budi Martami
  2. Mahasiswa mendaftarkan matakuliah, misalnya: @sayahadir regmk IK331
  3. Saat kuliah, mahasiswa presensi dengan tweet: @sayahadir hdr

Semua proses diatas semua dilakukan oleh mahasiswa secara mandiri sehingga memudahkan saya.   Reply dikirim untuk setiap transaksi. Dari sisi authorisasi, tentu saja sistem ini sama lemahnya sistem manual yang dapat menitipkan tandatangan. Mahasiswa dapat mengirimkan tweet dari tempat tidur saat jam kuliah atau bahkan membuat script yang mengirimkan tweet otomatis.  Memang bukan itu tujuannya. Pencegahannya dengan cara biasa, dipanggil satu-satu atau secara acak, dihitung dan diberi sanksi nilai E kalau curang.

Twitter dipilih karena memiliki  fasilitas pengiriman SMS untuk semua operator: Indosat, Telkomsel, XL, Three, Axis, Flexi dan Esia. Mahasiswa hanya cukup menggunakan  HP yang memiliki fasilitas SMS  (tidak perlu internet apalagi GPS).  Lagipula saya memang sedang tertarik dengan layanan Twitter ini (dalam rangka tweetmining)

Saat weekend saya mulai implementasi  ide ini (jadi harap maklum kalau code-nya masih berantakan, dikerjakan disela-sela waktu hehe).  Implementasinya sederhana. Ada tiga modul. Modul pertama untuk  ‘mendengarkan’  mention tweet dari mahasiswa yang berisi request pendaftaran (reg, regmk) dan kehadiran (hdr), mention ini kemudian disimpan ke dalam database. Modul kedua adalah melakukan pemrosesan (pendaftaran, kehadiran)  dan menyimpan responnya  di tabel.  Modul ketiga adalah membaca respon tersebut dan menjadikannya reply agar dapat dibaca mahasiswa.

Diperlukan satu account twitter  yang digunakan untuk  menerima permintaan dan memberikan reply.  Account ini dapat dianggap sebagai gerbang input-output komunikasi antar user dengan sistem.  Karena twitter dapat dijalankan  dengan SMS, dapat dianggap twitter ini sebagai SMS gateway. Jadi sebenarnya aplikasi presensi dapat digunakan untuk aplikasi-aplikasi lain.

Pertama kita siapkan account untuk ‘dikendalikan’ (saya pilih account @syhdr yang merupakan singkatan dari sayahadir). Kemudian  kita harus mendaftarkan aplikasi yang dapat membaca atau menulis account tersebut.  Daftarkan aplikasi ini lewat https://twitter.com/apps untuk mendapatkan CONSUMER_KEY dan CONSUMER_SECRET.

Untuk berkomunikasi dengan Twitter, saya gunakan  library twitteroauth.   Download dan baca file  DOCUMENTATION, index.php dan test.php.  Upload dan jalankan index.php atau test.php untuk melihat bagaimana library ini bekerja.  Langkah berikutnya adalalah menghubungkan antara aplikasi twitter  dengan account @syhdr.  Tambahkan dua baris dibawah $access_token = $_SESSION[‘access_token’]; pada file index.php

$access_token = $_SESSION['access_token'];
echo token=$access_token['oauth_token'];
echo token_secret=$access_token['oauth_token_secret'];

Token dan token secret adalah dua variabel yang dibutuhkan bagi sebuah aplikasi Twitter untuk dapat mengakses sebuah account Twitter. Jalankan program dan berikan ijin read/write untuk account @syhdr.    Catat dua variabel ini.

Setelah mendapatkan consumer_key, consumer_secret, token, dan token_secret sekarang kita dapat memulai. Semua variabel tersebut saya simpan dalam file conf.php:

<?php
define('CONSUMER_KEY', 'b5Hj------');
define('CONSUMER_SECRET', 'JiMaG----');
define('TOKEN','24635---');
define('TOKEN_SECRET','cJ591N---');
define('USER','----');
define('PASSWORD','---');
define('DATABASE','---');
?>

Modul pertama adalah membaca mention. Tweet yang berisi mention @syhdr akan dimasukkan untuk dproses lebih lanjut.  Setiap tweet memiliki ID yang unik,  ini perlu disimpan agar tidak ada dua tweet yang sama yang masuk ke database. Kemudian tanggal tweet juga berbeda dengan tanggal standard, sehingga perlu dikonversi dulu.

<?php
	//baca mentions dari account twiiter
	require_once('twitteroauth/twitteroauth.php');
	require_once('conf.php');

	function parseTwitterDate ($twDate) {
	  //parse twitter date ke date dan zone indonesia GMT+7 (string)
	  //contoh input: Fri Feb 04 10:12:27 +0000 2011
	  //contoh ouput: '2011-02-04 17:12:27'
	  $p = explode(" ",$twDate); //0: hari, 1: bulan, 2: tgl, 3:jam, 4 skip, 5: tahun
	  $strDate = "{$p[5]}-{$p[1]}-{$p[2]} {$p[3]}";
	  $phpDate   = strtotime($strDate)+(3600*7);  //tambah 7 jam
	  $mysqlDate = date('Y-m-d H:i:s',$phpDate);
	  return $mysqlDate;
	}

	$connection = new TwitterOAuth(CONSUMER_KEY, CONSUMER_SECRET, TOKEN, TOKEN_SECRET);
	$response = $connection->get('statuses/mentions');

	mysql_connect(localhost,USER,PASSWORD) or die ("Tidak dapat connect ke db");
	mysql_select_db(DATABASE) or die( "Tidak dapat select database");

	foreach ($response as $mention) {
		$user = $mention->user;
		//insert ke database, cek   berdasarkan id_str, kalau sudah ada, jangan dimasukkan
		$query = "SELECT count(*) FROM MENTION WHERE id_tweet = {$mention->id_str} LIMIT 1";
		$result=mysql_query($query);
		$tempRow = mysql_fetch_row($result);
		$num_rows = $tempRow[0];
		if ($num_rows==0)  { //belum ada
			//insert mention ke database
			$tweetDate = parseTwitterDate($mention->created_at);
			$qInsert = "INSERT INTO MENTION(ID_TWEET, TEXT, TIME, ID_USER_TWITTER, SCREEN_NAME) VALUES ({$mention->id_str},'{$mention->text}','$tweetDate',{$user->id_str},'{$user->screen_name}')";
			echo $qInsert;
			$result = mysql_query($qInsert);
			if (!$result) {
				die('Invalid query: ' . mysql_error());
			}
		}
	}
	mysql_close();
?>

Modul kedua adalah memproses mention dari mahasiswa. Contoh mention seperti “@syhdr reg 04001 Budi”. Kata kedua berisi perintah. “REG”  berarti pendaftaran mahasiswa (diproses oleh fungsi regMhs), “REGMK” berarti pendaftaran matakuliah (fungsi regMatakuliah) dan “HDR” berarti presensi dan diproses pada fungsi hadir. Respon dari setiap request kemudian disimpan di database.

<?php
	//syntax mention
	//1.   @syhdr reg NIM NAMA  --> daftar nim dan nama
	//      contoh: @syhdr reg 04002 encep fanda
	//2.   @syhdr regmk KODEMK
	//		 contoh: @syhdr regmk IK331
	//3.   @syhdr hdr

	//cek apa ada data yg perlu diproses, jika tidak, skip
	require_once('conf.php');
	mysql_connect(localhost,USER,PASSWORD) or die ("Tidak dapat connect ke db");
	mysql_select_db(DATABASE) or die( "Tidak dapat select database");

	$query = "select count(*) as JUM from MENTION where IS_PROSES=0 and SCREEN_NAME<>'syhdr'";
	$res=mysql_query($query);
	if (!$res) { die ("query error: $query");}
	$tempRow = mysql_fetch_row($res);
	$num_rows = $tempRow[0];
	if ($num_rows==0) {
	   echo 'tdk ada yg diproses, keluar';
	   exit();
	}

	function flagIsProsesMention($idMention) {
	   //tandai bahwa record sudah diproses di tabel mention
	   $query = "update MENTION set IS_PROSES=1 where ID_MENTION=$idMention";
	   $result=mysql_query($query);
	   if (!$result) { die ("query error: $query");}
	}

	function setReply($id_tweet,$id_user_twitter,$screen_name,$msg) {
	   //reply ke user, baik kalau perintah berhasil maupun salah
	   $query = "insert into  REPLY(ID_TWEET,ID_USER_TWITTER,SCREEN_NAME,TEXT) VALUES ($id_tweet,$id_user_twitter,'$screen_name','$msg')";
	   $result=mysql_query($query);
	   if (!$result) { die ("query error: $query"); }
	}

	function hadir($row) {
			//contoh: @syhdr hdr
			//cek apakah mhs sudah terdaftar di matakuliah dan matakuliah sesuai jadwal
			$query = "select mhs.ID_MHS, j.ID_JADWAL
			from
			  JADWAL j,
			  MHS_MATAKULIAH mm,
			  MHS mhs,
			  MATAKULIAH mk
			where
			  j.ID_MK = mk.ID_MK and
			  mm.ID_MK = mk.ID_MK and
			  mhs.ID_MHS = mm.ID_MHS and
			  mhs.ID_USER_TWITTER = {$row['ID_USER_TWITTER']} and
			  WEEKDAY('{$row['TIME']}') = j.WDAY and
			  TIME('{$row['TIME']}') >= j.START_TIME and
			  TIME('{$row['TIME']}') <= j.END_TIME
			";
			$result=mysql_query($query);
			if (!$result) { die ("query error: $query");}
			$tempRow = mysql_fetch_assoc($result);
			if (!$tempRow) {
				//ada 3 kemungkinan
				echo "mhs tdk terdaftar atu blm mendaftar mk atau jadwal belummulai/sudahselesai";
				setReply($row['ID_TWEET'],$row['ID_USER_TWITTER'],$row['SCREEN_NAME'],"gagal!mhs tdk terdaftr atau mk tdk terdaftr atau jdwl tdk ada;");
			} else {  //mhs terdaftar, mhs sudah reg matakuliah, jadwal ada yg cocok
			   //periksa apakah sudah ada di kehadiran?
			   $id_jadwal = $tempRow['ID_JADWAL'];
			   $id_mhs = $tempRow['ID_MHS'];
			   $query = "select count(*) from KEHADIRAN where ID_JADWAL = $id_jadwal and ID_MHS = $id_mhs";
			   $result=mysql_query($query);
			   if (!$result) { die ("query error: $query");}
			   $tempRow = mysql_fetch_row($result);
			   $num_rows = $tempRow[0];
			   if ($num_rows==0) { //tidak ada, insert
				    $query = "insert into KEHADIRAN(ID_JADWAL,ID_MHS,TIMESTAMP) values ($id_jadwal,$id_mhs,'{$row['TIME']}')";
					echo $query;
					$result=mysql_query($query);
					if (!$result) { die ("query error: $query");}
					setReply($row['ID_TWEET'],$row['ID_USER_TWITTER'],$row['SCREEN_NAME']," hadir!;");
			   } else
			   {
				  //batal, karena sudah
				  echo "{$row['SCREEN_NAME']} sudah memberikan presensi";
				  //tidak perlu direply karena percuma juga, dan mungkin banyak, lebih baik duplikasi diignore saja
			   }
			}
			flagIsProsesMention($row['ID_MENTION']);
	}

	function regMatakuliah($p,$row) {
			//contoh @syhdr regmk IK331
			//cek apakah sudah pernah mendaftar kuliah tsb
			$kodeMk = mysql_real_escape_string($p[2]);
			$query = "select count(*) from MHS_MATAKULIAH mm, MATAKULIAH mk, MHS where mm.ID_MHS=MHS.ID_MHS and mm.ID_MK = mk.ID_MK and mk.KODE_MK = '$kodeMk' and MHS.ID_USER_TWITTER = {$row['ID_USER_TWITTER']}";
			$result=mysql_query($query);
			if (!$result) { die ("query error: $query");}
			$tempRow = mysql_fetch_row($result);
			$num_rows = $tempRow[0];
			if ($num_rows==0) {  //tidak ada di tabel MHS_MATAKULIAH, add
				//cari ID matakuliah
				$query = "select ID_MK from MATAKULIAH where KODE_MK='$kodeMk'";
				$result=mysql_query($query);
				if (!$result) { die ("query error: $query");}
				$tempRow = mysql_fetch_row($result);
				if (!$tempRow)  { //matakuliah tdk terdaftar error
					echo "matakuliah $kodeMk tidak terdaftar";
					setReply($row['ID_TWEET'],$row['ID_USER_TWITTER'],$row['SCREEN_NAME'],"gagal! tdk ada mk $kodeMk;");
				} else
				{
					$id_mk = $tempRow[0];
					//cek apakah mahasiswa sudah terdaftar
					$query = "select ID_MHS from MHS where ID_USER_TWITTER={$row['ID_USER_TWITTER']}";
					$result=mysql_query($query);
					if (!$result) { die ("query error: $query");}
					$tempRow = mysql_fetch_row($result);
					if (!$tempRow)  { //mhs tdk terdaftar error
						echo "mhs, {$row['SCREEN_NAME']} tidak terdaftar";
						setReply($row['ID_TWEET'],$row['ID_USER_TWITTER'],$row['SCREEN_NAME'],"mhs atau twitter account belum terdaftar!;");
					} else {
						$id_mhs = $tempRow[0];
						$query = "insert into MHS_MATAKULIAH(ID_MHS,ID_MK,TGL_DAFTAR) values ($id_mhs,$id_mk,'{$row['TIME']}')";
						$result=mysql_query($query);
						if (!$result) { die ("query error: $query");}
						setReply($row['ID_TWEET'],$row['ID_USER_TWITTER'],$row['SCREEN_NAME'],"berhasil mendaftar ke $kodeMk;");
					}
				}

			} else
			{
			    //tdk dimasukkan, sudah terdaftar
				echo "matakuliah dan nim tsb sudah terdaftar";
				setReply($row['ID_TWEET'],$row['ID_USER_TWITTER'],$row['SCREEN_NAME'],"$kodeMk sudah terdaftar!");
			}
			// tandai di tabel mention bahwa sudah diproses
			flagIsProsesMention($row['ID_MENTION']);
	}

	function regMhs($p,$row) {
	   //contoh @syhdr reg NIM NAMA
	   if ($p[2]!=null && $p[3]!=null) {
		    //format sudah valid, bisa proses
			//p[2] = NIM p[3] dst = NAMA

			//cek apakah idtwitter atau NIM di tabel mhs sudah ada?
			$nim = mysql_real_escape_string($p[2]);
			$query = "select count(*) from MHS where ID_USER_TWITTER={$row['ID_USER_TWITTER']} OR NIM='$nim'";
			$result=mysql_query($query);
			if (!$result) { die ("query error: $query");}
			$tempRow = mysql_fetch_row($result);
			$num_rows = $tempRow[0];

			if ($num_rows==0) { //belum ada di tabel mhs, add
				//ambil nama lengkap
				for ($i=3;$i<sizeof($p);$i++) {
					$nama = $nama .' '. $p[$i];
				}
				$nama=mysql_real_escape_string($nama);
				//insert ke tabel mhs
				$query = "insert into MHS(NIM,NAMA,SCREEN_NAME,ID_USER_TWITTER) values ('$nim','$nama','{$row['SCREEN_NAME']}',{$row['ID_USER_TWITTER']})";
				$result=mysql_query($query);
				if (!$result) { die ("query error: $query");}
				echo $query."<br>";
				//add
				setReply($row['ID_TWEET'],$row['ID_USER_TWITTER'],$row['SCREEN_NAME'],"$nim sukses terdaftar;");
			} else {
			    //duplikasi id_twitter atau NIM
				echo "{$row['SCREEN_NAME']} atau NIM=$p[2] sudah terdaftar";
				setReply($row['ID_TWEET'],$row['ID_USER_TWITTER'],$row['SCREEN_NAME']," {$row['SCREEN_NAME']} atau  NIM=$p[2] sudah terdaftar;");
			}
		} else
	    {
			//reply format salah
			echo "format yang benar mendaftarkan diri adalah: @syhdr reg NIM NAMA";
			setReply($row['ID_TWEET'],$row['ID_USER_TWITTER'],$row['SCREEN_NAME'],"gagal!format yg bnr adl:@syhdr reg NIM NAMA;");
	    }
		// tandai di tabel mention bahwa sudah diproses
		flagIsProsesMention($row['ID_MENTION']);
	}

	//cek apakah ada data mention yg harus diproses
	$query = "select ID_MENTION,ID_TWEET,TEXT,TIME,ID_USER_TWITTER,SCREEN_NAME from MENTION where IS_PROSES=0 and SCREEN_NAME<>'syhdr' ORDER BY ID_TWEET";
	$result=mysql_query($query);
	if (!$result) { die ("query error: $query");}
	while ($row = mysql_fetch_assoc($result)) {
		 $text = trim(strtoupper($row['TEXT']));
		 echo $text."<br>";
		 $p = preg_split("/[ ]+/",$text); //explode gagal kalau ada lebih dari satu spasi
		 if ($p[0] =! '@SYHDR') {
		    //format error, kirim reply agar user menggunakan format yang benar
			setReply($row['ID_TWEET'],$row['ID_USER_TWITTER'],$row['SCREEN_NAME'],"perintah tdk dikenal!hrs diawali @syhdr");
			flagIsProsesMention($row['ID_MENTION']);
		 } else {
		    if ($p[1] == 'REG') {
			    //pendaftaran mhs
				echo "<br>reg mhs<br>";
				regMhs($p,$row);
			} elseif ($p[1] == 'REGMK') {
				//pendaftaran matakuliah
				echo "<br>reg matakuliah<br>";
				regMatakuliah($p,$row);
			} elseif ($p[1] == 'HDR') {
			    //presensei
				echo "<br>Hadir<br>";
				hadir($row);
			} else
			{
			   //error: kata kedua bukan reg,regmk, hdr
			   echo "<br>perintah tidak dikenal!<br>";
			   setReply($row['ID_TWEET'],$row['ID_USER_TWITTER'],$row['SCREEN_NAME'],"perintah tdk dikenal!kata kedua hrs regmk,reg,hdr;");
			   flagIsProsesMention($row['ID_MENTION']);
			}
		 }
	}

?>

Modul ketiga, membaca reply dari database dan mengirimkannya. Untuk menghemat (karena Twitter API membatasi jumlah komunikasi antara aplikasi dengan Twitter), maka beberapa reply dapat digabung dalam satu tweet.

<?php
	//baca mentions dari account twiiter
	require_once('twitteroauth/twitteroauth.php');
	require_once('conf.php');
	mysql_connect(localhost,USER,PASSWORD) or die ("Tidak dapat connect ke db");
	mysql_select_db(DATABASE) or die( "Tidak dapat select database");

	$stop = false;
	$msg = "";
	$sentID_Reply ="(";
	$query = "select ID_REPLY,ID_TWEET,TEXT,ID_USER_TWITTER,SCREEN_NAME from REPLY where IS_SENT=0 ORDER BY ID_TWEET";
	$result=mysql_query($query);
	if (!$result) { die ("query error: $query");}
	while (($row = mysql_fetch_assoc($result))  && !($stop)) {
		//ada reply harus dikirim
		$oldMsg = $msg;
		$msg =  $msg. '@'.$row['SCREEN_NAME']. " ".$row['TEXT']." ";
		if (strlen($msg) > 140 ) { //kelebihan, batal yg terakhir
			$msg = $oldMsg;
			$stop = true;
		} else { //cukup
		   $sentID_Reply = $sentID_Reply. $row['ID_REPLY'].",";
		}
	}

	if ($msg!="") { //ada yg perlu dikirim
		echo "<br> $msg <br>";
		$connection = new TwitterOAuth(CONSUMER_KEY, CONSUMER_SECRET, TOKEN, TOKEN_SECRET);
		$connection->post('statuses/update', array('status' => $msg));
		$sentID_Reply = $sentID_Reply. "-9999999)"; //dummy

		//update IS_SENT ke 1, artinya sudah diproses
		$query = "update REPLY set IS_SENT=1 where ID_REPLY IN ".$sentID_Reply;
		echo $query;
		$result=mysql_query($query);
		if (!$result) { die ("query error: $query");}
	} else {
	    echo "tidak ada yg perlu dikirim";
	}
?>

Terakhir, gunakan cron untuk memanggil ketiga modul ini (readMention, prosesMention dan sendReply) dalam periode tertentu. Ketiga modul ini dapat dijalankan secara async, tidak harus berurutan.

-update: sistem ini batal digunakan di kuliah saya. Secara teknis tidak bermasalah. Tapi ternyata banyak mahasiswa yang tidak memiliki smartphone dan SMS twitter belum reliable. Akibatnya saat awal kuliah mahasiswa sibuk saling pinjam handphone dan notebook.

3 Comments »

RSS feed for comments on this post. TrackBack URI

  1. haha. maaf oot,.
    tadi saya quote :

    “malas itu adalah ibu-nya dari penemuan “

    karena saya inget punya postingan ini: http://jurnal.snydez.com/id/1278/bangsa-pemalas-melahirkan-bangsa-kreatif

  2. Hehe.. sebenarnya yg umum itu: “Necessity is the mother of invention” ya dimodif dikit lah🙂

  3. Saya sangat tertarik dengan tulisan bapak ini. Saya mahasiswa informatika saya mohon ijin dan bimbingan buat coba sms gateway dengan twiter bapak. Mohon bimbingan……
    Jika bapak berkenan mohon sharing referensi ke email saya pak….
    terima kasih

    luck


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Create a free website or blog at WordPress.com.
Entries and comments feeds.

%d bloggers like this: