smslib 장문 문자 발송 수정
- 하마쿵
- 0
- 4,109
- 글주소
- 04-11
현재 사용되는 Smslib.php (아이코드) 모듈은 단문(80byte)만 가능합니다.
그래서 장문 발송가능한 아이코드 모듈을 토대로 수정했습니다.( sms_module4_php_v3.1.2_b25 )
기존 smslib 클래스에 멤버함수를 추가하고 수정하는 형태로 변경했습니다.
따라서 구버젼 함수, 신규 함수등이 모두 포함되어 있습니다.
(아이코드의 구 모듈에 신규모듈을 사용하는 방법을 상세히? 알려주는 pdf 파일을 참고.)
크게 바뀐부분
1. 아이코드 사이트에서 신규 토큰을 발급받아야 합니다.
2. 기존에 사용하던 id/pw 는 관리자와 연동되므로 계속 사용.
3. $this->Add() 멤버함수가 신규 함수로 교체.
4. 기타 .. (기억이 가믈가믈)
5. 관리자 sms 문자발송하기 테스트 할때
(views\admin\basic\sms\smssend\index.php) 자바스크립트 80byte 제약 해제필요.
-> byte_check() 함수 line 481 쯤에 return; if (cnt > 80) {..}
if문 앞에 return; 추가로 스킵하거나 다른게 변경하시면 됩니다.
(아래 코드는 신규 모듈 교체가 아닌 구버전을 하용해 장문발송이 가능하도록 수정한 소스입니다. )
필요하신분은 수정해서 사용하시면 됩니다.
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
/**
* Smslib class
*
* Copyright (c) CIBoard <www.ciboard.co.kr>
*
* @author CIBoard (develop@ciboard.co.kr)
*/
/**
* 문자 관련 class 입니다.
* 그누보드 (http://www.sir.co.kr) 의 소스 참조
*
* 아이코드 소켓 규격 v3.2.14 b45 (2018.1.31) 으로 대체 적용 - 하마쿵.2019.03.08.
* php용 sms_module4_php_v3.1.2_b25
*/
class Smslib extends CI_Controller
{
private $CI;
var $Log = array();
var $SMS_Server;
var $SMS_Port;
var $SMS_Key;
function __construct()
{
$this->CI = & get_instance();
$this->CI->load->helper( array('array'));
}
public function Init()
{
$this->Data = '';
$this->Result = '';
}
public function get_icode_info($id, $pw)
{
$result = get_sock('http://www.icodekorea.com/res/userinfo.php?userid=' . $id . '&userpw=' . $pw);
$res = explode(';', $result);
$userinfo = array(
'code' => $res[0], // 결과코드
'coin' => $res[1], // 고객 잔액 (충전제만 해당)
'gpay' => $res[2], // 고객의 건수 별 차감액 표시 (충전제만 해당)
'payment' => $res[3] // 요금제 표시, A:충전제, C:정액제
);
return $userinfo;
}
/**
* SMS 문자발송
* @param string $receiver - 수신번호
* @param string $sender - 발신번호
* @param string $content - 내용
* @param string $date - 예약일시
* @param string $memo - 제목
* @return string
*/
public function send($receiver = '', $sender = '', $content = '', $date = '', $memo = '')
{
$this->SMS_con();
$date2 = $date ? str_replace(array(' ', '-', ':'), '', $date) : '';
if (element('phone', $receiver)) {
$list[0] = $receiver;
} else {
$list = $receiver;
}
if ($list) {
foreach ($list as $key => $val) {
$list[$key]['phone'] = str_replace('-', '', element('phone', $val));
}
if (empty($list[$key]['phone'])) unset($list[$key]);
}
$send_phone = (element('phone', $sender)) ? element('phone', $sender) : '0000';
$send_mem_id = (element('mem_id', $sender)) ? element('mem_id', $sender) : '0';
$total = count($list);
if (empty($total)) {
$return = array(
'result' => 'fail',
'message' => '발송할 대상이 없습니다.'
);
return json_encode($return, JSON_UNESCAPED_UNICODE);
}
//$result = $this->Add($list, $send_phone, '', '', $content, $date2, $total);
$result = $this->Add($list, $send_phone, $content, $memo, $date2, $total);
if ($result) {
$result = $this->SendSMS();
$this->CI->load->model(array('Sms_send_content_model', 'Sms_send_history_model'));
if ($result) { //SMS 서버에 접속했습니다.
$contentdata = array(
'ssc_content' => $content,
'send_mem_id' => $send_mem_id,
'ssc_send_phone' => $send_phone,
'ssc_total' => $total,
'ssc_datetime' => cdate('Y-m-d H:i:s'),
'ssc_memo' => $memo,
);
if ($date) {
$contentdata['ssc_booking'] = cdate('Y-m-d H:i:s', strtotime($date));
}
$ssc_id = $this->CI->Sms_send_content_model->insert($contentdata);
$success = 0;
$fail = 0;
$count = 0;
$ssh_memo = '';
if ($this->Result) {
foreach ($this->Result as $result) {
$ssh_memo = '';
list($phone, $code) = explode(':', $result);
if (substr($code,0,5) === 'Error') {
$hs_code = substr($code,6,2);
switch ($hs_code) {
case '02': // '02:형식오류'
$ssh_memo = '형식이 잘못되어 전송이 실패하였습니다.';
break;
case '23': // '23:인증실패,데이터오류,전송날짜오류'
$ssh_memo = '데이터를 다시 확인해 주시기바랍니다.';
break;
// 아래의 사유들은 발송진행이 중단됨.
case '85': // "85:발송번호 미등록"
echo "등록되지 않는 발송번호 입니다.<br />";
break;
case '87': // "87:인증실패"
echo "(정액제-계약확인)인증 받지 못하였습니다.<br />";
break;
case '88': // "88:연동모듈 발송불가"
echo "연동모듈 사용이 불가능합니다. 아이코드로 문의하세요.<br />";
break;
case '96': // "96:토큰 검사 실패"
echo "사용할 수 없는 토큰키입니다.<br />";
break;
case '97': // '97:잔여코인부족'
$ssh_memo = '잔여코인이 부족합니다.';
break;
case '98': // '98:사용기간만료'
$ssh_memo = '사용기간이 만료되었습니다.';
break;
case '99': // '99:인증실패'
$ssh_memo = '인증 받지 못하였습니다. 계정을 다시 확인해 주세요.';
break;
default: // '미 확인 오류'
$ssh_memo = $hs_code.'알 수 없는 오류로 전송이 실패하였습니다.';
break;
}
$fail++;
$ssh_success = 0;
} else {
$hs_code = $code;
switch (substr($code,0,2)) {
case '17': // "17: 접수(발송)대기 처리. 지연해소시 발송됨."
//echo "접수(발송)대기처리 되었습니다.";
$ssh_memo = $phone . '접수(발송)대기처리 되었습니다.';
break;
default: // "00: 전송완료."
$ssh_memo = $phone . '로 전송했습니다.';
break;
}
$success++;
$ssh_success = 1;
}
$row = array_shift($list);
$row['phone'] = $row['phone'];
$log = array_shift($this->Log);
//$log = @iconv('UTF-8', 'UTF-8//IGNORE', $log); //주석처리.19.03.28.
$insertdata = array(
'ssc_id' => $ssc_id,
'send_mem_id' => $send_mem_id,
'recv_mem_id' => empty(element('mem_id', $row)) ? 0: element('mem_id', $row),
'ssh_name' => element('name', $row),
'ssh_phone' => element('phone', $row),
'ssh_success' => $ssh_success,
'ssh_datetime' => cdate('Y-m-d H:i:s'),
'ssh_memo' => $ssh_memo,
'ssh_log' => $log,
);
$this->CI->Sms_send_history_model->insert($insertdata);
}
}
$this->Init(); // 보관하고 있던 결과값을 지웁니다.
$updatedata = array(
'ssc_success' => $success,
'ssc_fail' => $fail,
);
$this->CI->Sms_send_content_model->update($ssc_id, $updatedata);
$return = array(
'result' => 'success',
'message' => '발송이 완료되었습니다.'
);
return json_encode($return, JSON_UNESCAPED_UNICODE);
} else {
$return = array(
'result' => 'fail',
'message' => '에러: SMS 서버와 통신이 불안정합니다.'
);
return json_encode($return, JSON_UNESCAPED_UNICODE);
}
} else {
$return = array(
'result' => 'fail',
'message' => '에러: SMS 데이터 입력도중 에러가 발생하였습니다.'
);
return json_encode($return, JSON_UNESCAPED_UNICODE);
}
}
// SMS 서버 접속
public function SMS_con()
{
$this->SMS_Server = '211.172.232.124';
$this->SMS_Port = $this->CI->cbconfig->item('sms_icode_port'); //ciboard에 설정되있음. (port: 9201)
$this->SMS_Key = $this->CI->cbconfig->item('sms_icode_key'); //관리자 sms 설정에 연동시킴. (발급받은 키값을 직접 넣어도됩니다.)
}
/**
* 발송번호의 값이 정확한 값인지 확인합니다.
*
* @param strDest 발송번호 배열입니다.
* nCount 배열의 크기입니다.
* @return 처리결과입니다.
*/
public function CheckCommonTypeDest($strDest, $nCount)
{
for ($i = 0; $i < $nCount; $i++) {
$hp_number = preg_replace("/[^0-9]/", '', $strDest[$i]['phone']);
if (strlen($hp_number) < 10 OR strlen($hp_number) > 11) {
return '휴대폰 번호가 틀렸습니다';
}
$CID = substr($hp_number,0,3);
if ( preg_match("/[^0-9]/", $CID) OR ($CID !== '010' && $CID !== '011' && $CID !== '016' && $CID !== '017' && $CID !== '018' && $CID !== '019')) {
return '휴대폰 앞자리 번호가 잘못되었습니다';
}
}
}
/**
* 회신번호의 값이 정확한 값인지 확인합니다.
*/
public function CheckCommonTypeCallBack($strCallBack)
{
if (preg_match("/[^0-9]/", $strCallBack)) {
return '회신 전화번호가 잘못되었습니다';
}
}
/**
* 예약날짜의 값이 정확한 값인지 확인합니다.
*/
public function CheckCommonTypeDate($strDate)
{
$strDate = preg_replace("/[^0-9]/", '', $strDate);
if ($strDate) {
if (checkdate(substr($strDate,4,2), substr($strDate,6,2), substr($strDate,0,4)) === false) {
return '예약날짜가 잘못되었습니다';
}
if (substr($strDate,8,2) >23 OR substr($strDate,10,2) >59) {
return '예약시간이 잘못되었습니다';
}
}
}
/**
* URL콜백용으로 메세지 크기를 수정합니다.
*
* @param url URL 내용입니다.
* msg 결과메시지입니다.
* desk 문자내용입니다.
*/
public function CheckCallCenter($url, $dest, $data)
{
switch (substr($dest,0,3)) {
case '010': //20바이트
return $this->cut_char($data,20);
break;
case '011': //80바이트
return $this->cut_char($data,80);
break;
case '016': // 80바이트
return $this->cut_char($data,80);
break;
case '017': // URL 포함 80바이트
return $this->cut_char($data,80 - strlen($url));
break;
case '018': // 20바이트
return $this->cut_char($data,20);
break;
case '019': // 20바이트
return $this->cut_char($data,20);
break;
default:
return $this->cut_char($data,80);
break;
}
}
/**
* 발송 패킷 생성
* Add(수신번호목록(배열), 발신번호, 발송내용(2000자이내), 제목(옵션, 30자이내), 예약일자(옵션, 12자리)
*/
public function Add($strDest, $strCallBack, $strMessage, $strSubject='', $strDate='', $nCount)
{
$strCallBack = str_replace('-', '', $strCallBack);
if(empty($strSubject)) $strSubject = $this->CI->cbconfig->item('site_title');
$Error = $this->CheckCommonTypeDest($strDest, $nCount); // 번호 검사
$Error = $this->CheckCommonTypeCallBack($strCallBack);
$Error = $this->CheckCommonTypeDate($strDate);
// 개행치환
$strData = preg_replace_callback("/\r\n/","fn_sms_chcr",$strMessage);
$strData = preg_replace_callback("/\r/","fn_sms_chcr",$strData);
// 문자 타입별 Port 설정.
$sendType = strlen($strData)>90 ? 1 : 0; // 0: SMS / 1: LMS
if($sendType==0) $strSubject = "";
$strCallBack = CutChar($strCallBack, 11); // 회신번호
/** LMS 제목 **/
/*
제목필드의 값이 없을 경우 단말기 기종및 설정에 따라 표기 방법이 다름
1.설정에서 제목필드보기 설정 Disable -> 제목필드값을 넣어도 미표기
2.설정에서 제목필드보기 설정 Enable -> 제목을 넣지 않을 경우 제목없음으로 자동표시
제목의 첫글자에 "<",">", 개행문자가 있을경우 단말기종류 및 통신사에 따라 메세지 전송실패 -> 글자를 체크하거나 취환처리요망
$strSubject = str_replace("\r\n", " ", $strSubject);
$strSubject = str_replace("<", "[", $strSubject);
$strSubject = str_replace(">", "]", $strSubject);
*/
$strSubject = CutCharUtf8($strSubject,30);
$strData = CutCharUtf8($strData,2000);
/* 필수 항목에 대해 정상적인 코드인지 검사 과정.
개발 방식에 따라 활용*/
//$Error = CheckCommonTypeDest($strDest); // 번호 검사
//$Error = IsVaildCallback($strCallBack);
//$Error = CheckCommonTypeDate($strDate);
for ($i = 0; $i < $nCount; $i++) {
$hp_number = $this->spacing($strDest[$i]['phone'],11);
if ( ! empty($strDest[$i]['name'])) {
$strData = str_replace('{이름}', $strDest[$i]['name'], $strData);
}
$list = array(
"key" => $this->SMS_Key,
"tel" => $hp_number,
"cb" => $strCallBack,
"msg" => $strData,
"charset" => 'utf-8'
);
if(!empty($strSubject)) $list['title'] = $strSubject;
if(!empty($strDate)) $list['date'] = $strDate;
$packet = json_encode($list, JSON_UNESCAPED_UNICODE);
$this->Data[$i] = '06'.str_pad(strlen($packet), 4, "0", STR_PAD_LEFT).$packet;
}
return true; // 수정대기
}
public function SendSMS()
{
$count = 1;
$is_test = false;
if ($is_test) {
if ($this->Data) {
foreach ($this->Data as $puts) {
if (rand(0,10)) {
$phone = substr($puts,26,11);
$code = '47022497 ';
} else {
$phone = substr($puts,26,11);
$code = 'Error(02)';
}
$this->Result[] = "$phone:$code";
$this->Log[] = $puts;
}
}
$this->Data = '';
return true;
}
$fsocket = fsockopen($this->SMS_Server, $this->SMS_Port, $errno, $errstr, 2);
if (empty($fsocket)) {
return false;
}
set_time_limit(300);
foreach ($this->Data as $puts) {
fputs($fsocket, $puts);
while(!$gets) { $gets = fgets($fsocket,32); }
$chk = preg_match("/\"tel\":\"([0-9]*)\"/", substr($puts,6), $matches);
$desc = $matches[1];
$resultCode = substr($gets,6,2);
if ($resultCode == '00' || $resultCode == '17') { // 17은 접수(발송)대기.
$this->Result[] = $resultCode.":".substr($gets,8,12).":".substr($gets,20,11);
$this->Log[] = '성공'.$puts;
} else {
$this->Result[] = $desc.":Error(".substr($gets,6,2).")";
$this->Log[] = '실패'.$puts;
if(substr($gets,6,2) >= "80") break;
}
$gets = "";
// 1천건씩 전송 후 5초 쉼
if ($count++%1000 === 0) {
sleep(5);
}
}
fclose($fsocket);
$this->Data = '';
return true;
}
public function spacing($text, $size)
{
for ($i = 0; $i < $size; $i++) {
$text .= ' ';
}
$text = substr($text,0, $size);
return $text;
}
public function cut_char($word, $cut)
{
$word = substr($word,0, $cut); // 필요한 길이만큼 취함.
for ($k = $cut-1; $k > 1; $k--) {
if (ord(substr($word, $k,1)) < 128) {
break; // 한글값은 160 이상.
}
}
$word = substr($word,0, $cut-($cut-$k+1)%2);
return $word;
}
public function CheckCommonType($dest, $rsvTime)
{
$dest = preg_replace("/[^0-9]/i", '', $dest);
if (strlen($dest) < 10 OR strlen($dest) >11) {
return '휴대폰 번호가 틀렸습니다';
}
$CID = substr($dest,0,3);
if ( preg_match("/[^0-9]/i", $CID) OR ($CID !== '010' && $CID !== '011' && $CID !== '016' && $CID !== '017' && $CID !== '018' && $CID !== '019')) {
return '휴대폰 앞자리 번호가 잘못되었습니다';
}
$rsvTime = preg_replace("/[^0-9]/i", '', $rsvTime);
if ($rsvTime) {
if (checkdate(substr($rsvTime,4,2), substr($rsvTime,6,2), substr($rsvTime,0,4)) === false) {
return '예약날짜가 잘못되었습니다';
}
if (substr($rsvTime,8,2) >23 OR substr($rsvTime,10,2) >59) {
return '예약시간이 잘못되었습니다';
}
}
}
}
// 개행치환
function fn_sms_chcr() { return "\n"; }
/**
* 원하는 문자열의 길이를 원하는 길이만큼 공백을 넣어 맞추도록 합니다.
*
* @param text 원하는 문자열입니다.
* size 원하는 길이입니다.
* @return 변경된 문자열을 넘깁니다.
*/
function FillSpace($text,$size) {
for ($i=0; $i<$size; $i++) $text.= " ";
$text = substr($text,0,$size);
return $text;
}
/**
* 원하는 문자열을 원하는 길에 맞는지 확인해서 조정하는 기능을 합니다.
*
* @param word 원하는 문자열입니다.
* cut 원하는 길이입니다.
* @return 변경된 문자열입니다.
*/
function CutChar($word, $cut) {
$word=substr($word,0,$cut); // 필요한 길이만큼 취함.
for ($k = $cut-1; $k > 1; $k--) {
if (ord(substr($word,$k,1))<128) break; // 한글값은 160 이상.
}
$word = substr($word, 0, $cut-($cut-$k+1)%2);
return $word;
}
function CutCharUtf8($word, $cut) {
preg_match_all('/[\xE0-\xFF][\x80-\xFF]{2}|./', $word, $match); // target for BMP
$m = $match[0];
$slen = strlen($word); // length of source string
if ($slen <= $cut) return $word;
$ret = array();
$count = 0;
for ($i=0; $i < $cut; $i++) {
$count += (strlen($m[$i]) > 1)?2:1;
if ($count > $cut) break;
$ret[] = $m[$i];
}
return join('', $ret);
}
/**
* 잘못된 수신번호 목록을 리턴합니다.
*
* @param strTelList 발송번호 배열.
* @return 잘못된 수신번호 목록.
*/
function CheckCommonTypeDest($strTelList) {
$result = '';
foreach ($strTelList as $tel) {
$tel = preg_replace("/[^0-9]/","",$tel);
if(!preg_match("/^(0[173][0136789])([0-9]{3,4})([0-9]{4})$/", $tel)) $result .= $tel.',';
}
return $result;
}
/**
* 회신번호 유효성 여부조회
* 한국인터넷진흥원 권고사항
*
* @param string callback 회신번호
* @return 처리결과입니다
*/
function IsVaildCallback($callback){
$_callback = preg_replace('/[^0-9]/', '', $callback);
if (!preg_match("/^(02|0[3-6]\d|01(0|1|3|5|6|7|8|9)|070|080|007)\-?\d{3,4}\-?\d{4,5}$/", $_callback) &&
!preg_match("/^(15|16|18)\d{2}\-?\d{4,5}$/", $_callback)) return "회신번호오류";
if (preg_match("/^(02|0[3-6]\d|01(0|1|3|5|6|7|8|9)|070|080)\-?0{3,4}\-?\d{4}$/", $_callback)) return "회신번호오류";
return '';
}
/**
* 문자열을 JSON 사용가능 타입으로 변환한다.
*/
function EscapeJsonString($value) {
$escapers = array('\\', '"');
$replacements = array('\\\\', '\"');
$result = str_replace($escapers, $replacements, $value);
return $result;
}
/**
* 예약날짜의 값이 정확한 값인지 확인합니다.
*
* @param string strDate 예약시간
* @return 처리결과입니다
*/
function CheckCommonTypeDate($strDate) {
$strDate = preg_replace("/[^0-9]/", "", $strDate);
if ($strDate){
if(strlen($strDate) != 12) return '예약날짜오류';
if (!checkdate(substr($strDate,4,2),substr($strDate,6,2),substr($rsvTime,0,4))) return "예약날짜오류";
if (substr($strDate,8,2)>23 || substr($strDate,10,2)>59) return "예약시간오류";
}
return '';
}